Skip to content

Commit 3b09939

Browse files
authored
Implement signal support (#27)
1 parent 752842f commit 3b09939

File tree

7 files changed

+369
-29
lines changed

7 files changed

+369
-29
lines changed

derive/src/attribute_ops.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ enum ExpEasingOpts {
252252
}
253253

254254
#[derive(FromField, Debug)]
255-
#[darling(forward_attrs(export, prop, doc))]
255+
#[darling(forward_attrs(export, prop, doc, signal))]
256256
pub struct FieldOpts {
257257
pub ident: Option<syn::Ident>,
258258
pub attrs: Vec<syn::Attribute>,

derive/src/lib.rs

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use type_paths::{godot_types, string_name_ty, variant_ty};
1717

1818
use crate::attribute_ops::{FieldExportOps, PropertyOpts};
1919

20-
#[proc_macro_derive(GodotScript, attributes(export, script, prop))]
20+
#[proc_macro_derive(GodotScript, attributes(export, script, prop, signal))]
2121
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2222
let input = parse_macro_input!(input as DeriveInput);
2323

@@ -41,8 +41,13 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
4141
|| field.attrs.iter().any(|attr| attr.path().is_ident("prop"))
4242
});
4343

44+
let signal_fields = fields.iter().filter(|field| {
45+
field.attrs.iter().any(|attr| attr.path().is_ident("signal"))
46+
});
47+
4448
let field_metadata_result: Result<TokenStream, TokenStream> = public_fields
4549
.clone()
50+
.filter(|field| !field.attrs.iter().any(|attr| attr.path().is_ident("signal")))
4651
.map(|field| {
4752
let name = field
4853
.ident
@@ -64,23 +69,7 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6469
ops.hint(field.ident.span())?
6570
};
6671

67-
let description = field
68-
.attrs
69-
.iter()
70-
.filter(|attr| attr.path().is_ident("doc"))
71-
.map(|attr| {
72-
attr.meta
73-
.require_name_value()
74-
.unwrap()
75-
.value
76-
.to_token_stream()
77-
})
78-
.reduce(|mut acc, comment| {
79-
acc.extend(quote!(, "\n", ));
80-
acc.extend(comment);
81-
acc
82-
});
83-
72+
let description = get_field_description(field);
8473
let item = quote! {
8574
::godot_rust_script::RustScriptPropDesc {
8675
name: #name,
@@ -101,7 +90,21 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10190
Err(err) => return err.into(),
10291
};
10392

104-
let get_fields_impl = derive_get_fields(public_fields.clone());
93+
let signal_metadata_result: TokenStream = signal_fields.clone().map(|field| {
94+
let signal_name = field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_default();
95+
let signal_description = get_field_description(field);
96+
let signal_type = &field.ty;
97+
98+
quote! {
99+
::godot_rust_script::RustScriptSignalDesc {
100+
name: #signal_name,
101+
arguments: <#signal_type as ::godot_rust_script::ScriptSignal>::argument_desc(),
102+
description: concat!(#signal_description),
103+
},
104+
}
105+
}).collect();
106+
107+
let get_fields_impl = derive_get_fields(public_fields.clone().chain(signal_fields));
105108
let set_fields_impl = derive_set_fields(public_fields.clone());
106109
let properties_state_impl = derive_property_states_export(public_fields);
107110
let default_impl = derive_default_with_base(&fields);
@@ -122,7 +125,7 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
122125
acc.extend(lit);
123126
acc
124127
});
125-
128+
126129
let output = quote! {
127130
impl ::godot_rust_script::GodotScript for #script_type_ident {
128131
#get_fields_impl
@@ -148,6 +151,9 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
148151
concat!(#description),
149152
vec![
150153
#field_metadata
154+
],
155+
vec![
156+
#signal_metadata_result
151157
]
152158
);
153159

@@ -205,8 +211,13 @@ fn derive_default_with_base(field_opts: &[FieldOpts]) -> TokenStream {
205211
.iter()
206212
.filter_map(|field| match field.ident.as_ref() {
207213
Some(ident) if *ident == "base" => {
208-
Some(quote_spanned!(ident.span() => #ident: base.cast(),))
214+
Some(quote_spanned!(ident.span() => #ident: base.clone().cast(),))
215+
},
216+
217+
Some(ident) if field.attrs.iter().any(|attr| attr.path().is_ident("signal")) => {
218+
Some(quote_spanned!(ident.span() => #ident: ::godot_rust_script::ScriptSignal::new(base.clone(), stringify!(#ident)),))
209219
}
220+
210221
Some(ident) => Some(quote_spanned!(ident.span() => #ident: Default::default(),)),
211222
None => None,
212223
})
@@ -335,6 +346,25 @@ fn derive_property_states_export<'a>(
335346
}
336347
}
337348

349+
fn get_field_description(field: &FieldOpts) -> Option<TokenStream> {
350+
field
351+
.attrs
352+
.iter()
353+
.filter(|attr| attr.path().is_ident("doc"))
354+
.map(|attr| {
355+
attr.meta
356+
.require_name_value()
357+
.unwrap()
358+
.value
359+
.to_token_stream()
360+
})
361+
.reduce(|mut acc, comment| {
362+
acc.extend(quote!(, "\n", ));
363+
acc.extend(comment);
364+
acc
365+
})
366+
}
367+
338368
#[proc_macro_attribute]
339369
pub fn godot_script_impl(
340370
args: proc_macro::TokenStream,

rust-script/src/library.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ pub use crate::script_registry::{
2222
};
2323
use crate::{
2424
apply::Apply,
25-
script_registry::{RemoteGodotScript_TO, RemoteScriptPropertyInfo},
25+
script_registry::{RemoteGodotScript_TO, RemoteScriptPropertyInfo, RemoteScriptSignalInfo},
2626
};
27+
pub use signals::{ScriptSignal, Signal, SignalArguments};
28+
29+
mod signals;
2730

2831
#[macro_export]
2932
macro_rules! register_script_class {
30-
($class_name:ty, $base_name:ty, $desc:expr, $props:expr) => {
33+
($class_name:ty, $base_name:ty, $desc:expr, $props:expr, $signals:expr) => {
3134
$crate::private_export::plugin_add! {
3235
SCRIPT_REGISTRY in $crate::private_export;
3336
$crate::RegistryItem::Entry($crate::RustScriptEntry {
@@ -36,6 +39,9 @@ macro_rules! register_script_class {
3639
properties: || {
3740
$props
3841
},
42+
signals: || {
43+
$signals
44+
},
3945
create_data: $crate::create_default_data_struct::<$class_name>,
4046
description: $desc,
4147
})
@@ -93,6 +99,7 @@ pub struct RustScriptEntry {
9399
pub class_name: &'static str,
94100
pub base_type_name: &'static str,
95101
pub properties: fn() -> Vec<RustScriptPropDesc>,
102+
pub signals: fn() -> Vec<RustScriptSignalDesc>,
96103
pub create_data: fn(Gd<Object>) -> RemoteGodotScript_TO<'static, RBox<()>>,
97104
pub description: &'static str,
98105
}
@@ -162,6 +169,26 @@ impl RustScriptMethodDesc {
162169
}
163170
}
164171

172+
pub struct RustScriptSignalDesc {
173+
pub name: &'static str,
174+
pub arguments: Vec<RustScriptPropDesc>,
175+
pub description: &'static str,
176+
}
177+
178+
impl From<RustScriptSignalDesc> for RemoteScriptSignalInfo {
179+
fn from(value: RustScriptSignalDesc) -> Self {
180+
Self {
181+
name: value.name.into(),
182+
arguments: value
183+
.arguments
184+
.into_iter()
185+
.map(|arg| arg.into_property_info("\0"))
186+
.collect(),
187+
description: value.description.into(),
188+
}
189+
}
190+
}
191+
165192
pub fn create_default_data_struct<T: GodotScript + 'static>(
166193
base: Gd<Object>,
167194
) -> RemoteGodotScript_TO<'static, RBox<()>> {
@@ -199,6 +226,8 @@ pub fn assemble_metadata<'a>(
199226
})
200227
.collect();
201228

229+
let signals = (class.signals)().into_iter().map(Into::into).collect();
230+
202231
let create_data = class.create_data;
203232
let description = class.description;
204233

@@ -207,6 +236,7 @@ pub fn assemble_metadata<'a>(
207236
class.base_type_name.into(),
208237
props,
209238
methods,
239+
signals,
210240
create_data,
211241
description.into(),
212242
)

0 commit comments

Comments
 (0)