Skip to content

Add groups export. #1214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Add groups export. #1214

wants to merge 3 commits into from

Conversation

Yarwin
Copy link
Contributor

@Yarwin Yarwin commented Jun 23, 2025

What does this PR solve?

Adds groups and subgroup.

How does it work?

I checked few options and finally went with a clap API (suggested by @ttencate) which requires specifying the group directly inside a #[export] attribute – everything else turned out to be too limiting, too messy or too janky 😒.

Example usage
#[derive(GodotClass)]
#[class(init, base=Node)]
struct Initializer {
    #[export(group = "g1765")]
    #[init( val = 124 )]
    pub rust_field_with_group_1: i32,

    pub rust_field_without_group_1: i32,

    #[export]
    pub export_without_group: i32,

    #[var]
    pub random_var: i64,

    #[export(subgroup = "some subgroup 2")]
    #[init( val = 444 )]
    pub export_with_subgroup3: i32,

    #[export(subgroup = "some subgroup")]
    pub export_with_subgroup: i32,
    #[export(subgroup = "some subgroup")]
    pub export_with_subgroup2: i32,

    #[export(range = (0.0, 10.0, or_greater), group = "g1765")]
    pub rust_field_with_group_7: f32,

    #[export(group = "g1", subgroup = "some subgroup")]
    pub export_with_group_and_subgroup: i32,

    #[export(group = "g2", subgroup = "some subgroup 2")]
    pub export_with_other_group_and_subgroup: i32,

    #[export(group = "g1", subgroup = "some subgroup 2")]
    pub export_with_yet_another_group_and_subgroup: i32,
    
    #[export(group = "g1765", subgroup = "some subgroup 3")]
    pub first_group_but_subgroup: i32,

    #[export(subgroup = "some subgroup 2")]
    #[init( val = 444 )]
    pub export_with_subgroup31: i32,

    base: Base<Node>,
}

image

Problems etc

Initially I wanted to ship it with struct extensions but they can be added in separate PR (I'll create the issue for that later). The leftovers of that is Fields struct being moved to separate file – I don't see anything wrong with that though, so I left it like that (not sure about named_fields function though).

I experimented with organizing fields into something along the lines of

struct Field {
    Parsed(Field),
    Group(GroupName),
    ClassExtension(ClassExtensionData)
}

but it turned out to be silly, unnecessary and unnecessary overcomplicated.

I am not really happy with GroupIdentifier (usize which simply points to some index holding group names) but it is kinda the sanest solution – idents make no sense (and use "to_string()" underneath anyway), strings require too many unnecessary allocations, *const String is… uh… let's not do that.

@Yarwin Yarwin added feature Adds functionality to the library c: register Register classes, functions and other symbols to GDScript labels Jun 23, 2025
@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1214

- Edit registered_docs.xml – from now on `export`ed fields are being documented after `#[var]` fields.
@Yarwin
Copy link
Contributor Author

Yarwin commented Jun 23, 2025

After change #[export] fields in docs are being included/documented after #[var], export-less fields 🤔. Godot docs by itself are inconsistent in this regard and don't specify any order, so I decided to keep it (feel free to complain and whatnot).

Example:

image

@ttencate
Copy link
Contributor

Looks like the docs are just using alphabetical order?

@Yarwin
Copy link
Contributor Author

Yarwin commented Jun 23, 2025

Coincidence – "a" is after "b" while all the items are not a #[export]s.

it went from:

<members>
// #[var]
<member name="item" type="f32" default="">this is very documented</member>
// export A & B
<member name="a" type="i32" default="" deprecated="use on your own risk!!">not to be confused with B!</member>
<member name="b" type="i64" default="" experimental="idk.">Some docs…</member>
// Two other #[var]s
<member name="item_2" type="i64" default="">is it documented?</member>
<member name="item_xml" type="GString" default="">this docstring has &lt; a special character</member>
</members>

for

pub struct FairlyDocumented {
#[doc = r#"this is very documented"#]
#[var]
item: f32,
#[doc = "@deprecated use on your own risk!!"]
#[doc = ""]
#[doc = "not to be confused with B!"]
#[export]
a: i32,
/// Some docs…
/// @experimental idk.
#[export]
b: i64,
/// is it documented?
#[var]
item_2: i64,
#[var]
/// this docstring has < a special character
item_xml: GString,
/// this isnt documented
_other_item: (),
/// nor this
base: Base<Node>,
}

to

<members>
// #[var]s
<member name="item" type="f32" default="">this is very documented</member>
<member name="item_2" type="i64" default="">is it documented?</member>
<member name="item_xml" type="GString" default="">this docstring has &lt; a special character</member>
// export A & B 
<member name="a" type="i32" default="" deprecated="use on your own risk!!">not to be confused with B!</member>
<member name="b" type="i64" default="" experimental="idk.">Some docs…</member>
</members>

I can change this order and document this quirk 🤔

EDIT: ah, now I see – you meant GODOT docs, not the test ones 😅 – it seems you are right (I checked it with other examples and yeah, they are sorted alphabetically).

- Formatting fixes, use `sort_by` instead of `sort_unstable_by` (as the already created comment states).
@TitanNano
Copy link
Contributor

Properties are implicitly added to the most recently defined group, like in GDScript, right? I'm not a fan of attributes having implicit effects on other properties.

@@ -749,6 +737,7 @@ fn parse_fields(
}

Ok(Fields {
groups,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you call format_groups here and pass the Vec by value? fn format_groups(groups: Vec<String>) -> Vec<String>

Comment on lines +41 to +47
pub(crate) fn new_from_kv(parser: &mut KvParser, groups: &mut Vec<String>) -> Self {
Self {
group_name_index: Self::parse_group("group", parser, groups),
subgroup_name_index: Self::parse_group("subgroup", parser, groups),
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently accepts any token-tree and just prays that it's a string litteral. This is also accepted:

    #[export(group = f64::MAX)]

but is turned into a group "f64 :: MAX" which is not something I'd expect.

Copy link
Contributor Author

@Yarwin Yarwin Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that it might be desirable, bigger issue might be typo such as #[export(group = g1765 range = (0.0, 10.0, or_greater))] (lack of comma) 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: register Register classes, functions and other symbols to GDScript feature Adds functionality to the library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants