Skip to content

Commit

Permalink
Merge pull request #852 from TitanNano/jovan/tool_editor_classes
Browse files Browse the repository at this point in the history
 Validate that editor plugin classes require `#[class(tool)]`
  • Loading branch information
Bromeon authored Aug 12, 2024
2 parents 9072095 + 09a2a8a commit 77500ba
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 5 deletions.
38 changes: 34 additions & 4 deletions godot-macros/src/class/derive_godot_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ fn parse_struct_attributes(class: &venial::Struct) -> ParseResult<ClassAttribute
parser.finish()?;
}

post_validate(&base_ty, is_tool)?;
post_validate(&base_ty, is_tool, is_editor_plugin)?;

Ok(ClassAttributes {
base_ty,
Expand Down Expand Up @@ -561,20 +561,36 @@ fn handle_opposite_keys(
}

/// Checks more logical combinations of attributes.
fn post_validate(base_ty: &Ident, is_tool: bool) -> ParseResult<()> {
fn post_validate(base_ty: &Ident, is_tool: bool, is_editor_plugin: bool) -> ParseResult<()> {
// TODO: this should be delegated to either:
// a) the type system: have a trait IsTool which is implemented when #[class(tool)] is set.
// Then, for certain base classes, require a tool bound (e.g. generate method `fn type_check<T: IsTool>()`).
// This would also allow moving the logic to godot-codegen.
// b) a runtime check in class.rs > register_class_raw() and validate_class_constraints().

let is_extension = is_class_virtual_extension(&base_ty.to_string());
if is_extension && !is_tool {
let class_name = base_ty.to_string();

let is_class_extension = is_class_virtual_extension(&class_name);
let is_class_editor = is_class_editor_only(&class_name);

if is_class_extension && !is_tool {
return bail!(
base_ty,
"Base class `{}` is a virtual extension class, which runs in the editor and thus requires #[class(tool)].",
base_ty
);
} else if class_name == "EditorPlugin" && !is_editor_plugin {
return bail!(
base_ty,
"Classes extending `{}` require #[class(editor_plugin)] to get registered as a plugin in the editor. See: https://godot-rust.github.io/book/recipes/editor-plugin/index.html",
base_ty
);
} else if is_class_editor && !is_tool {
return bail!(
base_ty,
"Base class `{}` is an editor-only class and thus requires #[class(tool)].",
base_ty
);
}

Ok(())
Expand All @@ -592,3 +608,17 @@ fn is_class_virtual_extension(godot_class_name: &str) -> bool {
_ => godot_class_name.ends_with("Extension"),
}
}

/// Whether a class exists primarily as a plugin for the editor.
// See post_validate(). Should be moved to godot-codegen > special_cases.rs.
// TODO: This information is available in extension_api.json under classes.*.api_type and should be taken from there.
fn is_class_editor_only(godot_class_name: &str) -> bool {
match godot_class_name {
"FileSystemDock" | "ScriptCreateDialog" | "ScriptEditor" | "ScriptEditorBase" => true,
_ => {
(godot_class_name.starts_with("ResourceImporter")
&& godot_class_name != "ResourceImporter")
|| godot_class_name.starts_with("Editor")
}
}
}
2 changes: 1 addition & 1 deletion itest/rust/src/register_tests/keyword_parameters_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use godot::obj::Gd;
use godot::register::{godot_api, GodotClass};

#[derive(GodotClass)]
#[class(base=EditorExportPlugin, init)]
#[class(base=EditorExportPlugin, init, tool)]
struct KeywordParameterEditorExportPlugin;

#[godot_api]
Expand Down

0 comments on commit 77500ba

Please sign in to comment.