From 6864eeeff9d6a4d2332afe12bd576a7fc4e22d0e Mon Sep 17 00:00:00 2001 From: ltabis Date: Tue, 19 Mar 2024 18:59:24 +0100 Subject: [PATCH 01/15] feat: add doc comments export for the CustomType derive macro --- codegen/src/custom_type.rs | 22 ++++++++++-- examples/custom_types_and_methods.rs | 4 +++ src/api/build_type.rs | 9 +++++ src/api/register.rs | 54 ++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/codegen/src/custom_type.rs b/codegen/src/custom_type.rs index 9517ba740..9e9eee07d 100644 --- a/codegen/src/custom_type.rs +++ b/codegen/src/custom_type.rs @@ -17,7 +17,7 @@ const OPTION_EXTRA: &str = "extra"; /// Derive the `CustomType` trait for a struct. pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream { - let type_name = input.ident; + let type_name = input.ident.clone(); let mut display_name = quote! { stringify!(#type_name) }; let mut field_accessors = Vec::new(); let mut extras = Vec::new(); @@ -112,11 +112,28 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream { } }; + let docs = { + #[cfg(feature = "metadata")] + { + let Ok(docs) = crate::attrs::doc_attributes(&input.attrs) else { + return syn::Error::new(Span::call_site(), "failed to parse doc comments") + .into_compile_error(); + }; + // Not sure how to make a Vec a literal, using a string instead. + let docs = proc_macro2::Literal::string(&docs.join("\n")); + quote! { builder.with_name_and_comments(#display_name, &#docs.lines().collect::>()[..]); } + } + #[cfg(not(feature = "metadata"))] + { + quote! { builder.with_name(#display_name); } + } + }; + quote! { impl CustomType for #type_name { fn build(mut builder: TypeBuilder) { #(#errors)* - builder.with_name(#display_name); + #docs #(#field_accessors)* #(#extras(&mut builder);)* } @@ -220,6 +237,7 @@ fn scan_fields(fields: &[&Field], accessors: &mut Vec, errors: &mut errors.push(syn::Error::new(path.span(), msg).into_compile_error()); continue; } + // Error _ => { errors.push( diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index 4dd0f5618..08fb844da 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -9,6 +9,8 @@ use rhai::{CustomType, Engine, EvalAltResult, TypeBuilder}; #[cfg(not(feature = "no_object"))] fn main() -> Result<(), Box> { + /// This is a test structure. If the metadata feature + /// is enabled, this comment will be exported. #[derive(Debug, Clone, CustomType)] #[rhai_type(extra = Self::build_extra)] struct TestStruct { @@ -47,6 +49,8 @@ fn main() -> Result<(), Box> { .into_iter() .for_each(|func| println!("{func}")); + println!("{}", engine.gen_fn_metadata_to_json(false).unwrap()); + println!(); } diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 0011bf25d..1a4d3f6cf 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -122,6 +122,15 @@ impl TypeBuilder<'_, T> { self } + /// Set a pretty-print name for the `type_of` function and comments. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_name_and_comments(&mut self, name: &str, comments: &[&str]) -> &mut Self { + self.engine + .register_type_with_name_and_comments::(name, comments); + self + } + /// Pretty-print this custom type. #[inline(always)] pub fn on_print( diff --git a/src/api/register.rs b/src/api/register.rs index 7a68988cb..753261c68 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -244,6 +244,60 @@ impl Engine { self } /// Register a custom type for use with the [`Engine`], with a pretty-print name + /// for the `type_of` function and comments. The type must implement [`Clone`], + /// and the "metadata" feature must be enabled. + /// + /// # Example + /// + /// ``` + /// #[derive(Clone)] + /// struct TestStruct { + /// field: i64 + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { + /// Self { field: 1 } + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new); + /// + /// assert_eq!( + /// engine.eval::("let x = new_ts(); type_of(x)")?, + /// "rust_out::TestStruct" + /// ); + /// + /// // Re-register the custom type with a name and comments. + /// engine.register_type_with_name_and_comments::("Hello", &vec!["A comment for this type"]); + /// + /// assert_eq!( + /// engine.eval::("let x = new_ts(); type_of(x)")?, + /// "Hello" + /// ); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn register_type_with_name_and_comments( + &mut self, + name: &str, + comments: &[&str], + ) -> &mut Self { + self.global_namespace_mut() + .set_custom_type_with_comments::(name, comments); + self + } + /// Register a custom type for use with the [`Engine`], with a pretty-print name /// for the `type_of` function. The type must implement [`Clone`]. /// /// # WARNING - Low Level API From acce906a742d06a0f689c2474b468f62b6b3d87e Mon Sep 17 00:00:00 2001 From: ltabis Date: Wed, 20 Mar 2024 11:59:56 +0100 Subject: [PATCH 02/15] feat: add documentation for fields in the CustomType derive macro --- codegen/src/custom_type.rs | 38 ++- examples/custom_types_and_methods.rs | 7 + src/api/build_type.rs | 125 ++++++++ src/api/register.rs | 457 +++++++++++++++++++++++++++ 4 files changed, 622 insertions(+), 5 deletions(-) diff --git a/codegen/src/custom_type.rs b/codegen/src/custom_type.rs index 9e9eee07d..1af2ac740 100644 --- a/codegen/src/custom_type.rs +++ b/codegen/src/custom_type.rs @@ -17,7 +17,7 @@ const OPTION_EXTRA: &str = "extra"; /// Derive the `CustomType` trait for a struct. pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream { - let type_name = input.ident.clone(); + let type_name = input.ident; let mut display_name = quote! { stringify!(#type_name) }; let mut field_accessors = Vec::new(); let mut extras = Vec::new(); @@ -112,7 +112,7 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream { } }; - let docs = { + let register = { #[cfg(feature = "metadata")] { let Ok(docs) = crate::attrs::doc_attributes(&input.attrs) else { @@ -133,7 +133,7 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream { impl CustomType for #type_name { fn build(mut builder: TypeBuilder) { #(#errors)* - #docs + #register #(#field_accessors)* #(#extras(&mut builder);)* } @@ -283,9 +283,37 @@ fn scan_fields(fields: &[&Field], accessors: &mut Vec, errors: &mut let name = map_name.unwrap_or_else(|| quote! { stringify!(#field_name) }); accessors.push(if readonly { - quote! { builder.with_get(#name, #get); } + #[cfg(feature = "metadata")] + { + let Ok(docs) = crate::attrs::doc_attributes(&field.attrs) else { + // return syn::Error::new(Span::call_site(), "failed to parse doc comments") + // .into_compile_error(); + todo!() + }; + // Not sure how to make a Vec a literal, using a string instead. + let docs = proc_macro2::Literal::string(&docs.join("\n")); + quote! { builder.with_get_and_comments(#name, &#docs.lines().collect::>()[..], #get) } + } + #[cfg(not(feature = "metadata"))] + { + quote! { builder.with_get(#name, #get); } + } } else { - quote! { builder.with_get_set(#name, #get, #set); } + #[cfg(feature = "metadata")] + { + let Ok(docs) = crate::attrs::doc_attributes(&field.attrs) else { + // return syn::Error::new(Span::call_site(), "failed to parse doc comments") + // .into_compile_error(); + todo!() + }; + // Not sure how to make a Vec a literal, using a string instead. + let docs = proc_macro2::Literal::string(&docs.join("\n")); + quote! { builder.with_get_set_and_comments(#name, &#docs.lines().collect::>()[..], #get, #set); } + } + #[cfg(not(feature = "metadata"))] + { + quote! { builder.with_get_set(#name, #get, #set); } + } }); } } diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index 08fb844da..7554e56ee 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -14,6 +14,13 @@ fn main() -> Result<(), Box> { #[derive(Debug, Clone, CustomType)] #[rhai_type(extra = Self::build_extra)] struct TestStruct { + /// A number. + /// + /// ```js + /// let t = new_ts(); + /// print(t.x); // Get the value of x. + /// t.x = 42; // Set the value of x. + /// ``` x: i64, } diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 1a4d3f6cf..4ef5a37f2 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -194,6 +194,24 @@ impl TypeBuilder<'_, T> { self } + /// Register a getter function with comments. + /// + /// The function signature must start with `&mut self` and not `&self`. + /// + /// Not available under `no_object`. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_get_and_comments( + &mut self, + name: impl AsRef, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, F> + SendSync + 'static, + ) -> &mut Self { + self.engine + .register_get_with_comments(name, comments, get_fn); + self + } + /// Register a setter function. /// /// Not available under `no_object`. @@ -207,6 +225,22 @@ impl TypeBuilder<'_, T> { self } + /// Register a setter function. + /// + /// Not available under `no_object`. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_set_and_comments( + &mut self, + name: impl AsRef, + comments: &[&str], + set_fn: impl RhaiNativeFunc<(Mut, R), 2, X, (), F> + SendSync + 'static, + ) -> &mut Self { + self.engine + .register_set_with_comments(name, comments, set_fn); + self + } + /// Short-hand for registering both getter and setter functions. /// /// All function signatures must start with `&mut self` and not `&self`. @@ -228,6 +262,31 @@ impl TypeBuilder<'_, T> { self.engine.register_get_set(name, get_fn, set_fn); self } + + /// Short-hand for registering both getter and setter functions. + /// + /// All function signatures must start with `&mut self` and not `&self`. + /// + /// Not available under `no_object`. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_get_set_and_comments< + const X1: bool, + const X2: bool, + R: Variant + Clone, + const F1: bool, + const F2: bool, + >( + &mut self, + name: impl AsRef, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut,), 1, X1, R, F1> + SendSync + 'static, + set_fn: impl RhaiNativeFunc<(Mut, R), 2, X2, (), F2> + SendSync + 'static, + ) -> &mut Self { + self.engine + .register_get_set_with_comments(name, comments, get_fn, set_fn); + self + } } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] @@ -251,6 +310,28 @@ impl TypeBuilder<'_, T> { self } + /// Register an index getter. + /// + /// The function signature must start with `&mut self` and not `&self`. + /// + /// Not available under both `no_index` and `no_object`. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_indexer_get_and_comments< + IDX: Variant + Clone, + const X: bool, + R: Variant + Clone, + const F: bool, + >( + &mut self, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X, R, F> + SendSync + 'static, + ) -> &mut Self { + self.engine + .register_indexer_get_with_comments(comments, get_fn); + self + } + /// Register an index setter. /// /// Not available under both `no_index` and `no_object`. @@ -268,6 +349,27 @@ impl TypeBuilder<'_, T> { self } + /// Register an index setter. + /// + /// Not available under both `no_index` and `no_object`. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_indexer_set_and_comments< + IDX: Variant + Clone, + const X: bool, + R: Variant + Clone, + const F: bool, + S: AsRef, + >( + &mut self, + comments: impl IntoIterator, + set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X, (), F> + SendSync + 'static, + ) -> &mut Self { + self.engine + .register_indexer_set_with_comments(comments, set_fn); + self + } + /// Short-hand for registering both index getter and setter functions. /// /// Not available under both `no_index` and `no_object`. @@ -287,4 +389,27 @@ impl TypeBuilder<'_, T> { self.engine.register_indexer_get_set(get_fn, set_fn); self } + + /// Short-hand for registering both index getter and setter functions. + /// + /// Not available under both `no_index` and `no_object`. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_indexer_get_set_and_comments< + IDX: Variant + Clone, + const X1: bool, + const X2: bool, + R: Variant + Clone, + const F1: bool, + const F2: bool, + >( + &mut self, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X1, R, F1> + SendSync + 'static, + set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X2, (), F2> + SendSync + 'static, + ) -> &mut Self { + self.engine + .register_indexer_get_set_with_comments(comments, get_fn, set_fn); + self + } } diff --git a/src/api/register.rs b/src/api/register.rs index 753261c68..a24df4e25 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -99,6 +99,82 @@ impl Engine { self } + /// Register a custom function with the [`Engine`]. + /// This method is only available with the metadata feature enabled. + /// + /// # Assumptions + /// + /// * **Accessibility**: The function namespace is [`FnNamespace::Global`][`crate::FnNamespace::Global`]. + /// + /// * **Purity**: The function is assumed to be _pure_ unless it is a property setter or an index setter. + /// + /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s). + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// // Normal function + /// fn add(x: i64, y: i64) -> i64 { + /// x + y + /// } + /// + /// let mut engine = Engine::new(); + /// + /// engine.register_fn_with_comments("add", &["add two numbers together"], add); + /// + /// assert_eq!(engine.eval::("add(40, 2)")?, 42); + /// + /// // You can also register a closure. + /// engine.register_fn_with_comments("sub", &["add two numbers together"], |x: i64, y: i64| x - y ); + /// + /// assert_eq!(engine.eval::("sub(44, 2)")?, 42); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[inline] + pub fn register_fn_with_comments< + A: 'static, + const N: usize, + const X: bool, + R: Variant + Clone, + const F: bool, + FUNC: RhaiNativeFunc + SendSync + 'static, + S: AsRef, + >( + &mut self, + name: impl AsRef + Into, + comments: impl IntoIterator, + func: FUNC, + ) -> &mut Self { + let reg = FuncRegistration::new(name.into()).in_global_namespace(); + + let reg = { + let mut param_type_names = FUNC::param_names() + .iter() + .map(|ty| format!("_: {}", self.format_param_type(ty))) + .collect::>(); + + if FUNC::return_type() != TypeId::of::<()>() { + param_type_names.push(self.format_param_type(FUNC::return_type_name()).into()); + } + + let param_type_names = param_type_names + .iter() + .map(String::as_str) + .collect::>(); + + reg.with_comments(comments) + .with_params_info(param_type_names) + }; + + reg.set_into_module(self.global_namespace_mut(), func); + + self + } /// Register a function of the [`Engine`]. /// /// # WARNING - Low Level API @@ -385,6 +461,63 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::make_getter(name.as_ref()), get_fn) } + + /// Register a getter function for a member of a registered type with the [`Engine`]. + /// + /// The function signature must start with `&mut self` and not `&self`. + /// + /// Not available under `no_object`. + /// + /// # Example + /// + /// ``` + /// #[derive(Clone)] + /// struct TestStruct { + /// field: i64 + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { + /// Self { field: 1 } + /// } + /// // Even a getter must start with `&mut self` and not `&self`. + /// fn get_field(&mut self) -> i64 { + /// self.field + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register a getter on a property (notice it doesn't have to be the same name). + /// .register_get("xyz", TestStruct::get_field); + /// + /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz")?, 1); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[cfg(not(feature = "no_object"))] + #[inline(always)] + pub fn register_get_with_comments< + T: Variant + Clone, + const X: bool, + R: Variant + Clone, + const F: bool, + >( + &mut self, + name: impl AsRef, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, F> + SendSync + 'static, + ) -> &mut Self { + self.register_fn_with_comments(crate::engine::make_getter(name.as_ref()), comments, get_fn) + } /// Register a setter function for a member of a registered type with the [`Engine`]. /// /// Not available under `no_object`. @@ -435,6 +568,63 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::make_setter(name.as_ref()), set_fn) } + /// Register a setter function for a member of a registered type with the [`Engine`]. + /// + /// Not available under `no_object`. + /// + /// # Example + /// + /// ``` + /// #[derive(Debug, Clone, Eq, PartialEq)] + /// struct TestStruct { + /// field: i64 + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { + /// Self { field: 1 } + /// } + /// fn set_field(&mut self, new_val: i64) { + /// self.field = new_val; + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register a setter on a property (notice it doesn't have to be the same name) + /// .register_set("xyz", TestStruct::set_field); + /// + /// // Notice that, with a getter, there is no way to get the property value + /// assert_eq!( + /// engine.eval::("let a = new_ts(); a.xyz = 42; a")?, + /// TestStruct { field: 42 } + /// ); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[cfg(not(feature = "no_object"))] + #[inline(always)] + pub fn register_set_with_comments< + T: Variant + Clone, + const X: bool, + R: Variant + Clone, + const F: bool, + >( + &mut self, + name: impl AsRef, + comments: &[&str], + set_fn: impl RhaiNativeFunc<(Mut, R), 2, X, (), F> + SendSync + 'static, + ) -> &mut Self { + self.register_fn_with_comments(crate::engine::make_setter(name.as_ref()), comments, set_fn) + } /// Short-hand for registering both getter and setter functions /// of a registered type with the [`Engine`]. /// @@ -497,6 +687,71 @@ impl Engine { ) -> &mut Self { self.register_get(&name, get_fn).register_set(&name, set_fn) } + /// Short-hand for registering both getter and setter functions + /// of a registered type with the [`Engine`]. + /// + /// All function signatures must start with `&mut self` and not `&self`. + /// + /// Not available under `no_object`. + /// + /// # Example + /// + /// ``` + /// #[derive(Clone)] + /// struct TestStruct { + /// field: i64 + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { + /// Self { field: 1 } + /// } + /// // Even a getter must start with `&mut self` and not `&self`. + /// fn get_field(&mut self) -> i64 { + /// self.field + /// } + /// fn set_field(&mut self, new_val: i64) { + /// self.field = new_val; + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// // Register API for the custom type. + /// engine + /// .register_type::() + /// .register_fn("new_ts", TestStruct::new) + /// // Register both a getter and a setter on a property + /// // (notice it doesn't have to be the same name) + /// .register_get_set("xyz", TestStruct::get_field, TestStruct::set_field); + /// + /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz = 42; a.xyz")?, 42); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[cfg(not(feature = "no_object"))] + #[inline(always)] + pub fn register_get_set_with_comments< + T: Variant + Clone, + const X1: bool, + const X2: bool, + R: Variant + Clone, + const F1: bool, + const F2: bool, + >( + &mut self, + name: impl AsRef, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut,), 1, X1, R, F1> + SendSync + 'static, + set_fn: impl RhaiNativeFunc<(Mut, R), 2, X2, (), F2> + SendSync + 'static, + ) -> &mut Self { + self.register_get_with_comments(&name, comments, get_fn) + .register_set_with_comments(&name, comments, set_fn) + } /// Register an index getter for a custom type with the [`Engine`]. /// /// The function signature must start with `&mut self` and not `&self`. @@ -560,6 +815,71 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::FN_IDX_GET, get_fn) } + /// Register an index getter for a custom type with the [`Engine`]. + /// + /// The function signature must start with `&mut self` and not `&self`. + /// + /// Not available under both `no_index` and `no_object`. + /// + /// # Panics + /// + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], + /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. + /// Indexers for arrays, object maps, strings and integers cannot be registered. + /// + /// # Example + /// + /// ``` + /// #[derive(Clone)] + /// struct TestStruct { + /// fields: Vec + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } + /// // Even a getter must start with `&mut self` and not `&self`. + /// fn get_field(&mut self, index: i64) -> i64 { + /// self.fields[index as usize] + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// // Register API for the custom type. + /// # #[cfg(not(feature = "no_object"))] + /// engine.register_type::(); + /// + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_get(TestStruct::get_field); + /// + /// # #[cfg(not(feature = "no_index"))] + /// assert_eq!(engine.eval::("let a = new_ts(); a[2]")?, 3); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[inline(always)] + pub fn register_indexer_get_with_comments< + T: Variant + Clone, + IDX: Variant + Clone, + const X: bool, + R: Variant + Clone, + const F: bool, + >( + &mut self, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X, R, F> + SendSync + 'static, + ) -> &mut Self { + self.register_fn_with_comments(crate::engine::FN_IDX_GET, comments, get_fn) + } /// Register an index setter for a custom type with the [`Engine`]. /// /// Not available under both `no_index` and `no_object`. @@ -623,6 +943,72 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::FN_IDX_SET, set_fn) } + /// Register an index setter for a custom type with the [`Engine`]. + /// + /// Not available under both `no_index` and `no_object`. + /// + /// # Panics + /// + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], + /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. + /// Indexers for arrays, object maps, strings and integers cannot be registered. + /// + /// # Example + /// + /// ``` + /// #[derive(Clone)] + /// struct TestStruct { + /// fields: Vec + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } + /// fn set_field(&mut self, index: i64, value: i64) { + /// self.fields[index as usize] = value; + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// // Register API for the custom type. + /// # #[cfg(not(feature = "no_object"))] + /// engine.register_type::(); + /// + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_set(TestStruct::set_field); + /// + /// # #[cfg(not(feature = "no_index"))] + /// let result = engine.eval::("let a = new_ts(); a[2] = 42; a")?; + /// + /// # #[cfg(not(feature = "no_index"))] + /// assert_eq!(result.fields[2], 42); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[inline(always)] + pub fn register_indexer_set_with_comments< + T: Variant + Clone, + IDX: Variant + Clone, + const X: bool, + R: Variant + Clone, + const F: bool, + S: AsRef, + >( + &mut self, + comments: impl IntoIterator, + set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X, (), F> + SendSync + 'static, + ) -> &mut Self { + self.register_fn_with_comments(crate::engine::FN_IDX_SET, comments, set_fn) + } /// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`]. /// /// Not available under both `no_index` and `no_object`. @@ -691,6 +1077,77 @@ impl Engine { self.register_indexer_get(get_fn) .register_indexer_set(set_fn) } + /// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`]. + /// + /// Not available under both `no_index` and `no_object`. + /// + /// # Panics + /// + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], + /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. + /// Indexers for arrays, object maps, strings and integers cannot be registered. + /// + /// # Example + /// + /// ``` + /// #[derive(Clone)] + /// struct TestStruct { + /// fields: Vec + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { + /// Self { fields: vec![1, 2, 3, 4, 5] } + /// } + /// // Even a getter must start with `&mut self` and not `&self`. + /// fn get_field(&mut self, index: i64) -> i64 { + /// self.fields[index as usize] + /// } + /// fn set_field(&mut self, index: i64, value: i64) { + /// self.fields[index as usize] = value; + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// // Register API for the custom type. + /// # #[cfg(not(feature = "no_object"))] + /// engine.register_type::(); + /// + /// engine + /// .register_fn("new_ts", TestStruct::new) + /// // Register an indexer. + /// .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field); + /// + /// # #[cfg(not(feature = "no_index"))] + /// assert_eq!(engine.eval::("let a = new_ts(); a[2] = 42; a[2]")?, 42); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "metadata")] + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[inline(always)] + pub fn register_indexer_get_set_with_comments< + T: Variant + Clone, + IDX: Variant + Clone, + const X1: bool, + const X2: bool, + R: Variant + Clone, + const F1: bool, + const F2: bool, + >( + &mut self, + comments: &[&str], + get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X1, R, F1> + SendSync + 'static, + set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X2, (), F2> + SendSync + 'static, + ) -> &mut Self { + self.register_indexer_get_with_comments(comments, get_fn) + .register_indexer_set_with_comments(comments, set_fn) + } + /// Register a shared [`Module`] into the global namespace of [`Engine`]. /// /// All functions and type iterators are automatically available to scripts without namespace From 4de8b6c7e0ee6fe7fd96bb18cf2570fa28d504ba Mon Sep 17 00:00:00 2001 From: ltabis Date: Wed, 20 Mar 2024 12:05:47 +0100 Subject: [PATCH 03/15] chore: handle error for fields doc generation --- codegen/src/custom_type.rs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/codegen/src/custom_type.rs b/codegen/src/custom_type.rs index 1af2ac740..04badea68 100644 --- a/codegen/src/custom_type.rs +++ b/codegen/src/custom_type.rs @@ -285,14 +285,17 @@ fn scan_fields(fields: &[&Field], accessors: &mut Vec, errors: &mut accessors.push(if readonly { #[cfg(feature = "metadata")] { - let Ok(docs) = crate::attrs::doc_attributes(&field.attrs) else { - // return syn::Error::new(Span::call_site(), "failed to parse doc comments") - // .into_compile_error(); - todo!() - }; - // Not sure how to make a Vec a literal, using a string instead. - let docs = proc_macro2::Literal::string(&docs.join("\n")); - quote! { builder.with_get_and_comments(#name, &#docs.lines().collect::>()[..], #get) } + match crate::attrs::doc_attributes(&field.attrs) { + Ok(docs) => { + // Not sure how to make a Vec a literal, using a string instead. + let docs = proc_macro2::Literal::string(&docs.join("\n")); + quote! { builder.with_get_and_comments(#name, &#docs.lines().collect::>()[..], #get); } + }, + Err(_) => { + errors.push(syn::Error::new(Span::call_site(), format!("failed to parse doc comments for field {}", quote! { #name })).into_compile_error()); + continue; + }, + } } #[cfg(not(feature = "metadata"))] { @@ -301,14 +304,17 @@ fn scan_fields(fields: &[&Field], accessors: &mut Vec, errors: &mut } else { #[cfg(feature = "metadata")] { - let Ok(docs) = crate::attrs::doc_attributes(&field.attrs) else { - // return syn::Error::new(Span::call_site(), "failed to parse doc comments") - // .into_compile_error(); - todo!() - }; - // Not sure how to make a Vec a literal, using a string instead. - let docs = proc_macro2::Literal::string(&docs.join("\n")); - quote! { builder.with_get_set_and_comments(#name, &#docs.lines().collect::>()[..], #get, #set); } + match crate::attrs::doc_attributes(&field.attrs) { + Ok(docs) => { + // Not sure how to make a Vec a literal, using a string instead. + let docs = proc_macro2::Literal::string(&docs.join("\n")); + quote! { builder.with_get_set_and_comments(#name, &#docs.lines().collect::>()[..], #get, #set); } + }, + Err(_) => { + errors.push(syn::Error::new(Span::call_site(), format!("failed to parse doc comments for field {}", quote! { #name })).into_compile_error()); + continue; + }, + } } #[cfg(not(feature = "metadata"))] { From e02d06a91e4c4d25ceebb5d921a0e9c05864f0d6 Mon Sep 17 00:00:00 2001 From: ltabis Date: Wed, 20 Mar 2024 13:26:51 +0100 Subject: [PATCH 04/15] test: add assertion in metadata examples --- examples/custom_types_and_methods.rs | 42 ++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index 7554e56ee..4a98fda01 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -56,9 +56,47 @@ fn main() -> Result<(), Box> { .into_iter() .for_each(|func| println!("{func}")); - println!("{}", engine.gen_fn_metadata_to_json(false).unwrap()); - println!(); + + let docs: serde_json::Value = + serde_json::from_str(&engine.gen_fn_metadata_to_json(false).unwrap()).unwrap(); + + // compare comments from the type. + assert_eq!( + docs["customTypes"][0]["docComments"], + serde_json::json!([ + "/// This is a test structure. If the metadata feature", + "/// is enabled, this comment will be exported." + ]) + ); + + // compare comments from the getter. + assert_eq!( + docs["functions"][1]["docComments"], + serde_json::json!([ + "/// A number.", + "///", + "/// ```js", + "/// let t = new_ts();", + "/// print(t.x); // Get the value of x.", + "/// t.x = 42; // Set the value of x.", + "/// ```" + ]) + ); + + // compare comments from the setter. + assert_eq!( + docs["functions"][3]["docComments"], + serde_json::json!([ + "/// A number.", + "///", + "/// ```js", + "/// let t = new_ts();", + "/// print(t.x); // Get the value of x.", + "/// t.x = 42; // Set the value of x.", + "/// ```" + ]) + ); } let result = engine.eval::( From 5e2905f3bf85c6182c1d6fa4e23517bf1ebb2594 Mon Sep 17 00:00:00 2001 From: ltabis Date: Wed, 20 Mar 2024 13:33:50 +0100 Subject: [PATCH 05/15] refactor: field get/set generator --- codegen/src/custom_type.rs | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/codegen/src/custom_type.rs b/codegen/src/custom_type.rs index 04badea68..33f67ad64 100644 --- a/codegen/src/custom_type.rs +++ b/codegen/src/custom_type.rs @@ -282,44 +282,34 @@ fn scan_fields(fields: &[&Field], accessors: &mut Vec, errors: &mut let set = set_fn.unwrap_or_else(|| quote! { |obj: &mut Self, val| obj.#field_name = val }); let name = map_name.unwrap_or_else(|| quote! { stringify!(#field_name) }); - accessors.push(if readonly { + accessors.push( #[cfg(feature = "metadata")] { match crate::attrs::doc_attributes(&field.attrs) { Ok(docs) => { // Not sure how to make a Vec a literal, using a string instead. let docs = proc_macro2::Literal::string(&docs.join("\n")); - quote! { builder.with_get_and_comments(#name, &#docs.lines().collect::>()[..], #get); } + if readonly { + quote! { builder.with_get_and_comments(#name, &#docs.lines().collect::>()[..], #get); } + } else { + quote! { builder.with_get_set_and_comments(#name, &#docs.lines().collect::>()[..], #get, #set); } + } + }, Err(_) => { errors.push(syn::Error::new(Span::call_site(), format!("failed to parse doc comments for field {}", quote! { #name })).into_compile_error()); continue; }, } - } + }, #[cfg(not(feature = "metadata"))] { - quote! { builder.with_get(#name, #get); } - } - } else { - #[cfg(feature = "metadata")] - { - match crate::attrs::doc_attributes(&field.attrs) { - Ok(docs) => { - // Not sure how to make a Vec a literal, using a string instead. - let docs = proc_macro2::Literal::string(&docs.join("\n")); - quote! { builder.with_get_set_and_comments(#name, &#docs.lines().collect::>()[..], #get, #set); } - }, - Err(_) => { - errors.push(syn::Error::new(Span::call_site(), format!("failed to parse doc comments for field {}", quote! { #name })).into_compile_error()); - continue; - }, + if readonly { + quote! { builder.with_get(#name, #get); } + } else { + quote! { builder.with_get_set(#name, #get, #set); } } - } - #[cfg(not(feature = "metadata"))] - { - quote! { builder.with_get_set(#name, #get, #set); } - } - }); + }, + ); } } From f64fdf07935f6fd462750ec4b94f65b052f5d6aa Mon Sep 17 00:00:00 2001 From: ltabis Date: Wed, 20 Mar 2024 13:44:23 +0100 Subject: [PATCH 06/15] docs: update metadata examples --- src/api/build_type.rs | 17 ++++++++++++----- src/api/register.rs | 26 +++++++++++++------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 4ef5a37f2..3ec03a092 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -123,6 +123,7 @@ impl TypeBuilder<'_, T> { } /// Set a pretty-print name for the `type_of` function and comments. + /// Available with the metadata feature only. #[cfg(feature = "metadata")] #[inline(always)] pub fn with_name_and_comments(&mut self, name: &str, comments: &[&str]) -> &mut Self { @@ -195,6 +196,7 @@ impl TypeBuilder<'_, T> { } /// Register a getter function with comments. + /// Available with the metadata feature only. /// /// The function signature must start with `&mut self` and not `&self`. /// @@ -225,7 +227,8 @@ impl TypeBuilder<'_, T> { self } - /// Register a setter function. + /// Register a setter function with comments. + /// Available with the metadata feature only. /// /// Not available under `no_object`. #[cfg(feature = "metadata")] @@ -263,7 +266,8 @@ impl TypeBuilder<'_, T> { self } - /// Short-hand for registering both getter and setter functions. + /// Short-hand for registering both getter and setter functions with comments. + /// Available with the metadata feature only. /// /// All function signatures must start with `&mut self` and not `&self`. /// @@ -310,7 +314,8 @@ impl TypeBuilder<'_, T> { self } - /// Register an index getter. + /// Register an index getter with comments. + /// Available with the metadata feature only. /// /// The function signature must start with `&mut self` and not `&self`. /// @@ -349,7 +354,8 @@ impl TypeBuilder<'_, T> { self } - /// Register an index setter. + /// Register an index setter with comments. + /// Available with the metadata feature only. /// /// Not available under both `no_index` and `no_object`. #[cfg(feature = "metadata")] @@ -390,7 +396,8 @@ impl TypeBuilder<'_, T> { self } - /// Short-hand for registering both index getter and setter functions. + /// Short-hand for registering both index getter and setter functions with comments. + /// Available with the metadata feature only. /// /// Not available under both `no_index` and `no_object`. #[cfg(feature = "metadata")] diff --git a/src/api/register.rs b/src/api/register.rs index a24df4e25..a18cac0b0 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -99,7 +99,7 @@ impl Engine { self } - /// Register a custom function with the [`Engine`]. + /// Register a custom function with the [`Engine`] with a set of comments. /// This method is only available with the metadata feature enabled. /// /// # Assumptions @@ -353,7 +353,7 @@ impl Engine { /// ); /// /// // Re-register the custom type with a name and comments. - /// engine.register_type_with_name_and_comments::("Hello", &vec!["A comment for this type"]); + /// engine.register_type_with_name_and_comments::("Hello", &["A comment for this type"]); /// /// assert_eq!( /// engine.eval::("let x = new_ts(); type_of(x)")?, @@ -462,7 +462,7 @@ impl Engine { self.register_fn(crate::engine::make_getter(name.as_ref()), get_fn) } - /// Register a getter function for a member of a registered type with the [`Engine`]. + /// Register a getter function for a member of a registered type with the [`Engine`] with comments. /// /// The function signature must start with `&mut self` and not `&self`. /// @@ -496,7 +496,7 @@ impl Engine { /// .register_type::() /// .register_fn("new_ts", TestStruct::new) /// // Register a getter on a property (notice it doesn't have to be the same name). - /// .register_get("xyz", TestStruct::get_field); + /// .register_get_with_comments("xyz", &["get the field"], TestStruct::get_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz")?, 1); /// # Ok(()) @@ -568,7 +568,7 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::make_setter(name.as_ref()), set_fn) } - /// Register a setter function for a member of a registered type with the [`Engine`]. + /// Register a setter function for a member of a registered type with the [`Engine`] with comments. /// /// Not available under `no_object`. /// @@ -599,7 +599,7 @@ impl Engine { /// .register_type::() /// .register_fn("new_ts", TestStruct::new) /// // Register a setter on a property (notice it doesn't have to be the same name) - /// .register_set("xyz", TestStruct::set_field); + /// .register_set_with_comments("xyz", &["setter comments"], TestStruct::set_field); /// /// // Notice that, with a getter, there is no way to get the property value /// assert_eq!( @@ -688,7 +688,7 @@ impl Engine { self.register_get(&name, get_fn).register_set(&name, set_fn) } /// Short-hand for registering both getter and setter functions - /// of a registered type with the [`Engine`]. + /// of a registered type with the [`Engine`] with comments. /// /// All function signatures must start with `&mut self` and not `&self`. /// @@ -726,7 +726,7 @@ impl Engine { /// .register_fn("new_ts", TestStruct::new) /// // Register both a getter and a setter on a property /// // (notice it doesn't have to be the same name) - /// .register_get_set("xyz", TestStruct::get_field, TestStruct::set_field); + /// .register_get_set_with_comments("xyz", &["comments"], TestStruct::get_field, TestStruct::set_field); /// /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz = 42; a.xyz")?, 42); /// # Ok(()) @@ -857,7 +857,7 @@ impl Engine { /// engine /// .register_fn("new_ts", TestStruct::new) /// // Register an indexer. - /// .register_indexer_get(TestStruct::get_field); + /// .register_indexer_get_with_comments(&["comments"], TestStruct::get_field); /// /// # #[cfg(not(feature = "no_index"))] /// assert_eq!(engine.eval::("let a = new_ts(); a[2]")?, 3); @@ -943,7 +943,7 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::FN_IDX_SET, set_fn) } - /// Register an index setter for a custom type with the [`Engine`]. + /// Register an index setter for a custom type with the [`Engine`] with comments. /// /// Not available under both `no_index` and `no_object`. /// @@ -982,7 +982,7 @@ impl Engine { /// engine /// .register_fn("new_ts", TestStruct::new) /// // Register an indexer. - /// .register_indexer_set(TestStruct::set_field); + /// .register_indexer_set_with_comments(&["comments"], TestStruct::set_field); /// /// # #[cfg(not(feature = "no_index"))] /// let result = engine.eval::("let a = new_ts(); a[2] = 42; a")?; @@ -1077,7 +1077,7 @@ impl Engine { self.register_indexer_get(get_fn) .register_indexer_set(set_fn) } - /// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`]. + /// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`] with comments. /// /// Not available under both `no_index` and `no_object`. /// @@ -1120,7 +1120,7 @@ impl Engine { /// engine /// .register_fn("new_ts", TestStruct::new) /// // Register an indexer. - /// .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field); + /// .register_indexer_get_set_with_comments(&["comments"], TestStruct::get_field, TestStruct::set_field); /// /// # #[cfg(not(feature = "no_index"))] /// assert_eq!(engine.eval::("let a = new_ts(); a[2] = 42; a[2]")?, 42); From 15f2f621e0bd34a5f86b2a167568b037206a4a5d Mon Sep 17 00:00:00 2001 From: ltabis Date: Wed, 20 Mar 2024 14:44:55 +0100 Subject: [PATCH 07/15] refactor: use the FuncRegistration API instead of adding *with_comments functions --- codegen/src/custom_type.rs | 1 - codegen/src/test/custom_type.rs | 91 +++++++ src/api/build_type.rs | 35 ++- src/api/register.rs | 456 -------------------------------- 4 files changed, 112 insertions(+), 471 deletions(-) diff --git a/codegen/src/custom_type.rs b/codegen/src/custom_type.rs index 33f67ad64..f966a1a0e 100644 --- a/codegen/src/custom_type.rs +++ b/codegen/src/custom_type.rs @@ -294,7 +294,6 @@ fn scan_fields(fields: &[&Field], accessors: &mut Vec, errors: &mut } else { quote! { builder.with_get_set_and_comments(#name, &#docs.lines().collect::>()[..], #get, #set); } } - }, Err(_) => { errors.push(syn::Error::new(Span::call_site(), format!("failed to parse doc comments for field {}", quote! { #name })).into_compile_error()); diff --git a/codegen/src/test/custom_type.rs b/codegen/src/test/custom_type.rs index 4853fc70d..705d494ef 100644 --- a/codegen/src/test/custom_type.rs +++ b/codegen/src/test/custom_type.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "metadata"))] #[cfg(test)] mod custom_type_tests { use crate::test::assert_streams_eq; @@ -85,3 +86,93 @@ mod custom_type_tests { assert_streams_eq(result, expected); } } + +#[cfg(feature = "metadata")] +#[cfg(test)] +mod custom_type_tests { + use crate::test::assert_streams_eq; + use quote::quote; + + #[test] + fn test_custom_type_tuple_struct() { + let input = quote! { + /// Bar comments. + #[derive(Clone, CustomType)] + pub struct Bar( + #[rhai_type(skip)] + #[cfg(not(feature = "no_float"))] + rhai::FLOAT, + INT, + #[rhai_type(name = "boo", readonly)] + String, + Vec + ); + }; + + let result = crate::custom_type::derive_custom_type_impl( + syn::parse2::(input).unwrap(), + ); + + let expected = quote! { + impl CustomType for Bar { + fn build(mut builder: TypeBuilder) { + builder.with_name_and_comments(stringify!(Bar)); + builder.with_get_set_and_comments("field1", + |obj: &mut Self| obj.1.clone(), + |obj: &mut Self, val| obj.1 = val + ); + builder.with_get_and_comments("boo", |obj: &mut Self| obj.2.clone()); + builder.with_get_set_and_comments("field3", + |obj: &mut Self| obj.3.clone(), + |obj: &mut Self, val| obj.3 = val + ); + } + } + }; + + assert_streams_eq(result, expected); + } + + #[test] + fn test_custom_type_struct() { + let input = quote! { + #[derive(CustomType)] + #[rhai_type(skip, name = "MyFoo", extra = Self::build_extra)] + pub struct Foo { + #[cfg(not(feature = "no_float"))] + #[rhai_type(skip)] + _dummy: rhai::FLOAT, + #[rhai_type(get = get_bar)] + pub bar: INT, + #[rhai_type(name = "boo", readonly)] + pub(crate) baz: String, + #[rhai_type(set = Self::set_qux)] + pub qux: Vec + } + }; + + let result = crate::custom_type::derive_custom_type_impl( + syn::parse2::(input).unwrap(), + ); + + let expected = quote! { + impl CustomType for Foo { + fn build(mut builder: TypeBuilder) { + builder.with_name_and_comments("MyFoo"); + builder.with_get_set_and_comments(stringify!(bar), + |obj: &mut Self| get_bar(&*obj), + |obj: &mut Self, val| obj.bar = val + ); + builder.with_get_and_comments("boo", |obj: &mut Self| obj.baz.clone()); + builder.with_get_set_and_comments(stringify!(qux), + |obj: &mut Self| obj.qux.clone(), + Self::set_qux + ); + Self::build_extra(&mut builder); + } + } + }; + + assert_streams_eq(result, expected); + } +} diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 3ec03a092..de6dbc7fc 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -3,12 +3,16 @@ use crate::func::SendSync; use crate::packages::string_basic::{FUNC_TO_DEBUG, FUNC_TO_STRING}; use crate::{types::dynamic::Variant, Engine, Identifier, RhaiNativeFunc}; use std::marker::PhantomData; + #[cfg(feature = "no_std")] use std::prelude::v1::*; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] use crate::func::register::Mut; +#[cfg(feature = "metadata")] +use crate::FuncRegistration; + /// Trait to build the API of a custom type for use with an [`Engine`] /// (i.e. register the type and its getters, setters, methods, etc.). /// @@ -209,8 +213,9 @@ impl TypeBuilder<'_, T> { comments: &[&str], get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, F> + SendSync + 'static, ) -> &mut Self { - self.engine - .register_get_with_comments(name, comments, get_fn); + FuncRegistration::new_getter(name) + .with_comments(comments) + .register_into_engine(self.engine, get_fn); self } @@ -239,8 +244,9 @@ impl TypeBuilder<'_, T> { comments: &[&str], set_fn: impl RhaiNativeFunc<(Mut, R), 2, X, (), F> + SendSync + 'static, ) -> &mut Self { - self.engine - .register_set_with_comments(name, comments, set_fn); + FuncRegistration::new_setter(name) + .with_comments(comments) + .register_into_engine(self.engine, set_fn); self } @@ -287,9 +293,8 @@ impl TypeBuilder<'_, T> { get_fn: impl RhaiNativeFunc<(Mut,), 1, X1, R, F1> + SendSync + 'static, set_fn: impl RhaiNativeFunc<(Mut, R), 2, X2, (), F2> + SendSync + 'static, ) -> &mut Self { - self.engine - .register_get_set_with_comments(name, comments, get_fn, set_fn); - self + self.with_get_and_comments(&name, comments, get_fn) + .with_set_and_comments(&name, comments, set_fn) } } @@ -332,8 +337,10 @@ impl TypeBuilder<'_, T> { comments: &[&str], get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X, R, F> + SendSync + 'static, ) -> &mut Self { - self.engine - .register_indexer_get_with_comments(comments, get_fn); + FuncRegistration::new_index_getter() + .with_comments(comments) + .register_into_engine(self.engine, get_fn); + self } @@ -371,8 +378,9 @@ impl TypeBuilder<'_, T> { comments: impl IntoIterator, set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X, (), F> + SendSync + 'static, ) -> &mut Self { - self.engine - .register_indexer_set_with_comments(comments, set_fn); + FuncRegistration::new_index_setter() + .with_comments(comments) + .register_into_engine(self.engine, set_fn); self } @@ -415,8 +423,7 @@ impl TypeBuilder<'_, T> { get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X1, R, F1> + SendSync + 'static, set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X2, (), F2> + SendSync + 'static, ) -> &mut Self { - self.engine - .register_indexer_get_set_with_comments(comments, get_fn, set_fn); - self + self.with_indexer_get_and_comments(comments, get_fn) + .with_indexer_set_and_comments(comments, set_fn) } } diff --git a/src/api/register.rs b/src/api/register.rs index a18cac0b0..7b88b9abb 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -99,82 +99,6 @@ impl Engine { self } - /// Register a custom function with the [`Engine`] with a set of comments. - /// This method is only available with the metadata feature enabled. - /// - /// # Assumptions - /// - /// * **Accessibility**: The function namespace is [`FnNamespace::Global`][`crate::FnNamespace::Global`]. - /// - /// * **Purity**: The function is assumed to be _pure_ unless it is a property setter or an index setter. - /// - /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s). - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// // Normal function - /// fn add(x: i64, y: i64) -> i64 { - /// x + y - /// } - /// - /// let mut engine = Engine::new(); - /// - /// engine.register_fn_with_comments("add", &["add two numbers together"], add); - /// - /// assert_eq!(engine.eval::("add(40, 2)")?, 42); - /// - /// // You can also register a closure. - /// engine.register_fn_with_comments("sub", &["add two numbers together"], |x: i64, y: i64| x - y ); - /// - /// assert_eq!(engine.eval::("sub(44, 2)")?, 42); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[inline] - pub fn register_fn_with_comments< - A: 'static, - const N: usize, - const X: bool, - R: Variant + Clone, - const F: bool, - FUNC: RhaiNativeFunc + SendSync + 'static, - S: AsRef, - >( - &mut self, - name: impl AsRef + Into, - comments: impl IntoIterator, - func: FUNC, - ) -> &mut Self { - let reg = FuncRegistration::new(name.into()).in_global_namespace(); - - let reg = { - let mut param_type_names = FUNC::param_names() - .iter() - .map(|ty| format!("_: {}", self.format_param_type(ty))) - .collect::>(); - - if FUNC::return_type() != TypeId::of::<()>() { - param_type_names.push(self.format_param_type(FUNC::return_type_name()).into()); - } - - let param_type_names = param_type_names - .iter() - .map(String::as_str) - .collect::>(); - - reg.with_comments(comments) - .with_params_info(param_type_names) - }; - - reg.set_into_module(self.global_namespace_mut(), func); - - self - } /// Register a function of the [`Engine`]. /// /// # WARNING - Low Level API @@ -462,62 +386,6 @@ impl Engine { self.register_fn(crate::engine::make_getter(name.as_ref()), get_fn) } - /// Register a getter function for a member of a registered type with the [`Engine`] with comments. - /// - /// The function signature must start with `&mut self` and not `&self`. - /// - /// Not available under `no_object`. - /// - /// # Example - /// - /// ``` - /// #[derive(Clone)] - /// struct TestStruct { - /// field: i64 - /// } - /// - /// impl TestStruct { - /// fn new() -> Self { - /// Self { field: 1 } - /// } - /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self) -> i64 { - /// self.field - /// } - /// } - /// - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let mut engine = Engine::new(); - /// - /// // Register API for the custom type. - /// engine - /// .register_type::() - /// .register_fn("new_ts", TestStruct::new) - /// // Register a getter on a property (notice it doesn't have to be the same name). - /// .register_get_with_comments("xyz", &["get the field"], TestStruct::get_field); - /// - /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz")?, 1); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[cfg(not(feature = "no_object"))] - #[inline(always)] - pub fn register_get_with_comments< - T: Variant + Clone, - const X: bool, - R: Variant + Clone, - const F: bool, - >( - &mut self, - name: impl AsRef, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, F> + SendSync + 'static, - ) -> &mut Self { - self.register_fn_with_comments(crate::engine::make_getter(name.as_ref()), comments, get_fn) - } /// Register a setter function for a member of a registered type with the [`Engine`]. /// /// Not available under `no_object`. @@ -568,63 +436,6 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::make_setter(name.as_ref()), set_fn) } - /// Register a setter function for a member of a registered type with the [`Engine`] with comments. - /// - /// Not available under `no_object`. - /// - /// # Example - /// - /// ``` - /// #[derive(Debug, Clone, Eq, PartialEq)] - /// struct TestStruct { - /// field: i64 - /// } - /// - /// impl TestStruct { - /// fn new() -> Self { - /// Self { field: 1 } - /// } - /// fn set_field(&mut self, new_val: i64) { - /// self.field = new_val; - /// } - /// } - /// - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let mut engine = Engine::new(); - /// - /// // Register API for the custom type. - /// engine - /// .register_type::() - /// .register_fn("new_ts", TestStruct::new) - /// // Register a setter on a property (notice it doesn't have to be the same name) - /// .register_set_with_comments("xyz", &["setter comments"], TestStruct::set_field); - /// - /// // Notice that, with a getter, there is no way to get the property value - /// assert_eq!( - /// engine.eval::("let a = new_ts(); a.xyz = 42; a")?, - /// TestStruct { field: 42 } - /// ); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[cfg(not(feature = "no_object"))] - #[inline(always)] - pub fn register_set_with_comments< - T: Variant + Clone, - const X: bool, - R: Variant + Clone, - const F: bool, - >( - &mut self, - name: impl AsRef, - comments: &[&str], - set_fn: impl RhaiNativeFunc<(Mut, R), 2, X, (), F> + SendSync + 'static, - ) -> &mut Self { - self.register_fn_with_comments(crate::engine::make_setter(name.as_ref()), comments, set_fn) - } /// Short-hand for registering both getter and setter functions /// of a registered type with the [`Engine`]. /// @@ -687,71 +498,6 @@ impl Engine { ) -> &mut Self { self.register_get(&name, get_fn).register_set(&name, set_fn) } - /// Short-hand for registering both getter and setter functions - /// of a registered type with the [`Engine`] with comments. - /// - /// All function signatures must start with `&mut self` and not `&self`. - /// - /// Not available under `no_object`. - /// - /// # Example - /// - /// ``` - /// #[derive(Clone)] - /// struct TestStruct { - /// field: i64 - /// } - /// - /// impl TestStruct { - /// fn new() -> Self { - /// Self { field: 1 } - /// } - /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self) -> i64 { - /// self.field - /// } - /// fn set_field(&mut self, new_val: i64) { - /// self.field = new_val; - /// } - /// } - /// - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let mut engine = Engine::new(); - /// - /// // Register API for the custom type. - /// engine - /// .register_type::() - /// .register_fn("new_ts", TestStruct::new) - /// // Register both a getter and a setter on a property - /// // (notice it doesn't have to be the same name) - /// .register_get_set_with_comments("xyz", &["comments"], TestStruct::get_field, TestStruct::set_field); - /// - /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz = 42; a.xyz")?, 42); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[cfg(not(feature = "no_object"))] - #[inline(always)] - pub fn register_get_set_with_comments< - T: Variant + Clone, - const X1: bool, - const X2: bool, - R: Variant + Clone, - const F1: bool, - const F2: bool, - >( - &mut self, - name: impl AsRef, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut,), 1, X1, R, F1> + SendSync + 'static, - set_fn: impl RhaiNativeFunc<(Mut, R), 2, X2, (), F2> + SendSync + 'static, - ) -> &mut Self { - self.register_get_with_comments(&name, comments, get_fn) - .register_set_with_comments(&name, comments, set_fn) - } /// Register an index getter for a custom type with the [`Engine`]. /// /// The function signature must start with `&mut self` and not `&self`. @@ -815,71 +561,6 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::FN_IDX_GET, get_fn) } - /// Register an index getter for a custom type with the [`Engine`]. - /// - /// The function signature must start with `&mut self` and not `&self`. - /// - /// Not available under both `no_index` and `no_object`. - /// - /// # Panics - /// - /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], - /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. - /// Indexers for arrays, object maps, strings and integers cannot be registered. - /// - /// # Example - /// - /// ``` - /// #[derive(Clone)] - /// struct TestStruct { - /// fields: Vec - /// } - /// - /// impl TestStruct { - /// fn new() -> Self { - /// Self { fields: vec![1, 2, 3, 4, 5] } - /// } - /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self, index: i64) -> i64 { - /// self.fields[index as usize] - /// } - /// } - /// - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let mut engine = Engine::new(); - /// - /// // Register API for the custom type. - /// # #[cfg(not(feature = "no_object"))] - /// engine.register_type::(); - /// - /// engine - /// .register_fn("new_ts", TestStruct::new) - /// // Register an indexer. - /// .register_indexer_get_with_comments(&["comments"], TestStruct::get_field); - /// - /// # #[cfg(not(feature = "no_index"))] - /// assert_eq!(engine.eval::("let a = new_ts(); a[2]")?, 3); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[inline(always)] - pub fn register_indexer_get_with_comments< - T: Variant + Clone, - IDX: Variant + Clone, - const X: bool, - R: Variant + Clone, - const F: bool, - >( - &mut self, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X, R, F> + SendSync + 'static, - ) -> &mut Self { - self.register_fn_with_comments(crate::engine::FN_IDX_GET, comments, get_fn) - } /// Register an index setter for a custom type with the [`Engine`]. /// /// Not available under both `no_index` and `no_object`. @@ -943,72 +624,6 @@ impl Engine { ) -> &mut Self { self.register_fn(crate::engine::FN_IDX_SET, set_fn) } - /// Register an index setter for a custom type with the [`Engine`] with comments. - /// - /// Not available under both `no_index` and `no_object`. - /// - /// # Panics - /// - /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], - /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. - /// Indexers for arrays, object maps, strings and integers cannot be registered. - /// - /// # Example - /// - /// ``` - /// #[derive(Clone)] - /// struct TestStruct { - /// fields: Vec - /// } - /// - /// impl TestStruct { - /// fn new() -> Self { - /// Self { fields: vec![1, 2, 3, 4, 5] } - /// } - /// fn set_field(&mut self, index: i64, value: i64) { - /// self.fields[index as usize] = value; - /// } - /// } - /// - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let mut engine = Engine::new(); - /// - /// // Register API for the custom type. - /// # #[cfg(not(feature = "no_object"))] - /// engine.register_type::(); - /// - /// engine - /// .register_fn("new_ts", TestStruct::new) - /// // Register an indexer. - /// .register_indexer_set_with_comments(&["comments"], TestStruct::set_field); - /// - /// # #[cfg(not(feature = "no_index"))] - /// let result = engine.eval::("let a = new_ts(); a[2] = 42; a")?; - /// - /// # #[cfg(not(feature = "no_index"))] - /// assert_eq!(result.fields[2], 42); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[inline(always)] - pub fn register_indexer_set_with_comments< - T: Variant + Clone, - IDX: Variant + Clone, - const X: bool, - R: Variant + Clone, - const F: bool, - S: AsRef, - >( - &mut self, - comments: impl IntoIterator, - set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X, (), F> + SendSync + 'static, - ) -> &mut Self { - self.register_fn_with_comments(crate::engine::FN_IDX_SET, comments, set_fn) - } /// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`]. /// /// Not available under both `no_index` and `no_object`. @@ -1077,77 +692,6 @@ impl Engine { self.register_indexer_get(get_fn) .register_indexer_set(set_fn) } - /// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`] with comments. - /// - /// Not available under both `no_index` and `no_object`. - /// - /// # Panics - /// - /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], - /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. - /// Indexers for arrays, object maps, strings and integers cannot be registered. - /// - /// # Example - /// - /// ``` - /// #[derive(Clone)] - /// struct TestStruct { - /// fields: Vec - /// } - /// - /// impl TestStruct { - /// fn new() -> Self { - /// Self { fields: vec![1, 2, 3, 4, 5] } - /// } - /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self, index: i64) -> i64 { - /// self.fields[index as usize] - /// } - /// fn set_field(&mut self, index: i64, value: i64) { - /// self.fields[index as usize] = value; - /// } - /// } - /// - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let mut engine = Engine::new(); - /// - /// // Register API for the custom type. - /// # #[cfg(not(feature = "no_object"))] - /// engine.register_type::(); - /// - /// engine - /// .register_fn("new_ts", TestStruct::new) - /// // Register an indexer. - /// .register_indexer_get_set_with_comments(&["comments"], TestStruct::get_field, TestStruct::set_field); - /// - /// # #[cfg(not(feature = "no_index"))] - /// assert_eq!(engine.eval::("let a = new_ts(); a[2] = 42; a[2]")?, 42); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[inline(always)] - pub fn register_indexer_get_set_with_comments< - T: Variant + Clone, - IDX: Variant + Clone, - const X1: bool, - const X2: bool, - R: Variant + Clone, - const F1: bool, - const F2: bool, - >( - &mut self, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X1, R, F1> + SendSync + 'static, - set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X2, (), F2> + SendSync + 'static, - ) -> &mut Self { - self.register_indexer_get_with_comments(comments, get_fn) - .register_indexer_set_with_comments(comments, set_fn) - } - /// Register a shared [`Module`] into the global namespace of [`Engine`]. /// /// All functions and type iterators are automatically available to scripts without namespace From cb2f8e0d8f02a7de678ec9fa2d08e4fc8efebdc7 Mon Sep 17 00:00:00 2001 From: ltabis Date: Wed, 20 Mar 2024 14:52:51 +0100 Subject: [PATCH 08/15] test: update codegen tests when using metadata feature --- codegen/src/test/custom_type.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/codegen/src/test/custom_type.rs b/codegen/src/test/custom_type.rs index 705d494ef..00e5a3be7 100644 --- a/codegen/src/test/custom_type.rs +++ b/codegen/src/test/custom_type.rs @@ -103,8 +103,10 @@ mod custom_type_tests { #[cfg(not(feature = "no_float"))] rhai::FLOAT, INT, + /// boo comments. #[rhai_type(name = "boo", readonly)] String, + /// This is a vector. Vec ); }; @@ -116,13 +118,15 @@ mod custom_type_tests { let expected = quote! { impl CustomType for Bar { fn build(mut builder: TypeBuilder) { - builder.with_name_and_comments(stringify!(Bar)); + builder.with_name_and_comments(stringify!(Bar), &"/// Bar comments.".lines().collect::>()[..]); builder.with_get_set_and_comments("field1", + &"".lines().collect::>()[..], |obj: &mut Self| obj.1.clone(), |obj: &mut Self, val| obj.1 = val ); - builder.with_get_and_comments("boo", |obj: &mut Self| obj.2.clone()); + builder.with_get_and_comments("boo", &"/// boo comments.".lines().collect::>()[..], |obj: &mut Self| obj.2.clone()); builder.with_get_set_and_comments("field3", + &"/// This is a vector.".lines().collect::>()[..], |obj: &mut Self| obj.3.clone(), |obj: &mut Self, val| obj.3 = val ); @@ -136,6 +140,7 @@ mod custom_type_tests { #[test] fn test_custom_type_struct() { let input = quote! { + /// Foo comments. #[derive(CustomType)] #[rhai_type(skip, name = "MyFoo", extra = Self::build_extra)] pub struct Foo { @@ -144,6 +149,7 @@ mod custom_type_tests { _dummy: rhai::FLOAT, #[rhai_type(get = get_bar)] pub bar: INT, + /// boo comments. #[rhai_type(name = "boo", readonly)] pub(crate) baz: String, #[rhai_type(set = Self::set_qux)] @@ -158,13 +164,14 @@ mod custom_type_tests { let expected = quote! { impl CustomType for Foo { fn build(mut builder: TypeBuilder) { - builder.with_name_and_comments("MyFoo"); - builder.with_get_set_and_comments(stringify!(bar), + builder.with_name_and_comments("MyFoo", &"/// Foo comments.".lines().collect::>()[..]); + builder.with_get_set_and_comments(stringify!(bar), &"".lines().collect::>()[..], |obj: &mut Self| get_bar(&*obj), |obj: &mut Self, val| obj.bar = val ); - builder.with_get_and_comments("boo", |obj: &mut Self| obj.baz.clone()); + builder.with_get_and_comments("boo", &"/// boo comments.".lines().collect::>()[..], |obj: &mut Self| obj.baz.clone()); builder.with_get_set_and_comments(stringify!(qux), + &"".lines().collect::>()[..], |obj: &mut Self| obj.qux.clone(), Self::set_qux ); From 4bee70cd346924556b4b4ec07ff7f4e54d6acf8c Mon Sep 17 00:00:00 2001 From: ltabis Date: Fri, 22 Mar 2024 14:20:26 +0100 Subject: [PATCH 09/15] refactor: keep function hashes in cach to add comments later in the building process --- codegen/src/custom_type.rs | 41 ++++--- codegen/src/test/custom_type.rs | 25 ++-- src/api/build_type.rs | 201 +++++++++----------------------- src/module/mod.rs | 23 ++++ 4 files changed, 116 insertions(+), 174 deletions(-) diff --git a/codegen/src/custom_type.rs b/codegen/src/custom_type.rs index f966a1a0e..bb909cc5c 100644 --- a/codegen/src/custom_type.rs +++ b/codegen/src/custom_type.rs @@ -282,33 +282,38 @@ fn scan_fields(fields: &[&Field], accessors: &mut Vec, errors: &mut let set = set_fn.unwrap_or_else(|| quote! { |obj: &mut Self, val| obj.#field_name = val }); let name = map_name.unwrap_or_else(|| quote! { stringify!(#field_name) }); - accessors.push( + accessors.push({ + let method = if readonly { + quote! { builder.with_get(#name, #get) } + } else { + quote! { builder.with_get_set(#name, #get, #set) } + }; + #[cfg(feature = "metadata")] { match crate::attrs::doc_attributes(&field.attrs) { Ok(docs) => { // Not sure how to make a Vec a literal, using a string instead. let docs = proc_macro2::Literal::string(&docs.join("\n")); - if readonly { - quote! { builder.with_get_and_comments(#name, &#docs.lines().collect::>()[..], #get); } - } else { - quote! { builder.with_get_set_and_comments(#name, &#docs.lines().collect::>()[..], #get, #set); } - } - }, + quote! { #method.and_comments(&#docs.lines().collect::>()[..]); } + } Err(_) => { - errors.push(syn::Error::new(Span::call_site(), format!("failed to parse doc comments for field {}", quote! { #name })).into_compile_error()); + errors.push( + syn::Error::new( + Span::call_site(), + format!( + "failed to parse doc comments for field {}", + quote! { #name } + ), + ) + .into_compile_error(), + ); continue; - }, + } } - }, + } #[cfg(not(feature = "metadata"))] - { - if readonly { - quote! { builder.with_get(#name, #get); } - } else { - quote! { builder.with_get_set(#name, #get, #set); } - } - }, - ); + quote! { #method; } + }); } } diff --git a/codegen/src/test/custom_type.rs b/codegen/src/test/custom_type.rs index 00e5a3be7..8fededbdf 100644 --- a/codegen/src/test/custom_type.rs +++ b/codegen/src/test/custom_type.rs @@ -119,17 +119,16 @@ mod custom_type_tests { impl CustomType for Bar { fn build(mut builder: TypeBuilder) { builder.with_name_and_comments(stringify!(Bar), &"/// Bar comments.".lines().collect::>()[..]); - builder.with_get_set_and_comments("field1", - &"".lines().collect::>()[..], + builder.with_get_set("field1", |obj: &mut Self| obj.1.clone(), |obj: &mut Self, val| obj.1 = val - ); - builder.with_get_and_comments("boo", &"/// boo comments.".lines().collect::>()[..], |obj: &mut Self| obj.2.clone()); - builder.with_get_set_and_comments("field3", - &"/// This is a vector.".lines().collect::>()[..], + ).and_comments(&"".lines().collect::>()[..]); + builder.with_get("boo", |obj: &mut Self| obj.2.clone()) + .and_comments(&"/// boo comments.".lines().collect::>()[..]); + builder.with_get_set("field3", |obj: &mut Self| obj.3.clone(), |obj: &mut Self, val| obj.3 = val - ); + ).and_comments(&"/// This is a vector.".lines().collect::>()[..]); } } }; @@ -165,16 +164,16 @@ mod custom_type_tests { impl CustomType for Foo { fn build(mut builder: TypeBuilder) { builder.with_name_and_comments("MyFoo", &"/// Foo comments.".lines().collect::>()[..]); - builder.with_get_set_and_comments(stringify!(bar), &"".lines().collect::>()[..], + builder.with_get_set(stringify!(bar), |obj: &mut Self| get_bar(&*obj), |obj: &mut Self, val| obj.bar = val - ); - builder.with_get_and_comments("boo", &"/// boo comments.".lines().collect::>()[..], |obj: &mut Self| obj.baz.clone()); - builder.with_get_set_and_comments(stringify!(qux), - &"".lines().collect::>()[..], + ).and_comments(&"".lines().collect::>()[..]); + builder.with_get("boo", |obj: &mut Self| obj.baz.clone()) + .and_comments(&"/// boo comments.".lines().collect::>()[..]); + builder.with_get_set(stringify!(qux), |obj: &mut Self| obj.qux.clone(), Self::set_qux - ); + ).and_comments(&"".lines().collect::>()[..]); Self::build_extra(&mut builder); } } diff --git a/src/api/build_type.rs b/src/api/build_type.rs index de6dbc7fc..24e5b3926 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -1,6 +1,8 @@ //! Trait to build a custom type for use with [`Engine`]. use crate::func::SendSync; +use crate::module::FuncMetadata; use crate::packages::string_basic::{FUNC_TO_DEBUG, FUNC_TO_STRING}; +use crate::FuncRegistration; use crate::{types::dynamic::Variant, Engine, Identifier, RhaiNativeFunc}; use std::marker::PhantomData; @@ -10,9 +12,6 @@ use std::prelude::v1::*; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] use crate::func::register::Mut; -#[cfg(feature = "metadata")] -use crate::FuncRegistration; - /// Trait to build the API of a custom type for use with an [`Engine`] /// (i.e. register the type and its getters, setters, methods, etc.). /// @@ -104,6 +103,8 @@ impl Engine { /// to use [`Engine::register_type_with_name`] instead. pub struct TypeBuilder<'a, T: Variant + Clone> { engine: &'a mut Engine, + /// Keep the latest registered function(s) in cache to add additional metadata. + hashes: Option>, _marker: PhantomData, } @@ -113,6 +114,7 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> { fn new(engine: &'a mut Engine) -> Self { Self { engine, + hashes: None, _marker: PhantomData, } } @@ -142,7 +144,9 @@ impl TypeBuilder<'_, T> { &mut self, on_print: impl Fn(&mut T) -> String + SendSync + 'static, ) -> &mut Self { - self.engine.register_fn(FUNC_TO_STRING, on_print); + let FuncMetadata { hash, .. } = + FuncRegistration::new(FUNC_TO_STRING).register_into_engine(self.engine, on_print); + self.hashes = Some(vec![*hash]); self } @@ -150,9 +154,11 @@ impl TypeBuilder<'_, T> { #[inline(always)] pub fn on_debug( &mut self, - on_print: impl Fn(&mut T) -> String + SendSync + 'static, + on_debug: impl Fn(&mut T) -> String + SendSync + 'static, ) -> &mut Self { - self.engine.register_fn(FUNC_TO_DEBUG, on_print); + let FuncMetadata { hash, .. } = + FuncRegistration::new(FUNC_TO_DEBUG).register_into_engine(self.engine, on_debug); + self.hashes = Some(vec![*hash]); self } @@ -163,7 +169,24 @@ impl TypeBuilder<'_, T> { name: impl AsRef + Into, method: impl RhaiNativeFunc + SendSync + 'static, ) -> &mut Self { - self.engine.register_fn(name, method); + let FuncMetadata { hash, .. } = + FuncRegistration::new(name).register_into_engine(self.engine, method); + self.hashes = Some(vec![*hash]); + self + } + + /// Add comments to the last registered function. + /// Available under the metadata feature only. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn and_comments(&mut self, comments: &[&str]) -> &mut Self { + if let Some(hashes) = &self.hashes { + let module = self.engine.global_namespace_mut(); + + for hash in hashes { + module.update_fn_comments(*hash, comments); + } + } self } } @@ -195,27 +218,10 @@ impl TypeBuilder<'_, T> { name: impl AsRef, get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, F> + SendSync + 'static, ) -> &mut Self { - self.engine.register_get(name, get_fn); - self - } + let FuncMetadata { hash, .. } = + FuncRegistration::new_getter(name).register_into_engine(self.engine, get_fn); + self.hashes = Some(vec![*hash]); - /// Register a getter function with comments. - /// Available with the metadata feature only. - /// - /// The function signature must start with `&mut self` and not `&self`. - /// - /// Not available under `no_object`. - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn with_get_and_comments( - &mut self, - name: impl AsRef, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, F> + SendSync + 'static, - ) -> &mut Self { - FuncRegistration::new_getter(name) - .with_comments(comments) - .register_into_engine(self.engine, get_fn); self } @@ -228,25 +234,10 @@ impl TypeBuilder<'_, T> { name: impl AsRef, set_fn: impl RhaiNativeFunc<(Mut, R), 2, X, (), F> + SendSync + 'static, ) -> &mut Self { - self.engine.register_set(name, set_fn); - self - } + let FuncMetadata { hash, .. } = + FuncRegistration::new_setter(name).register_into_engine(self.engine, set_fn); + self.hashes = Some(vec![*hash]); - /// Register a setter function with comments. - /// Available with the metadata feature only. - /// - /// Not available under `no_object`. - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn with_set_and_comments( - &mut self, - name: impl AsRef, - comments: &[&str], - set_fn: impl RhaiNativeFunc<(Mut, R), 2, X, (), F> + SendSync + 'static, - ) -> &mut Self { - FuncRegistration::new_setter(name) - .with_comments(comments) - .register_into_engine(self.engine, set_fn); self } @@ -268,33 +259,15 @@ impl TypeBuilder<'_, T> { get_fn: impl RhaiNativeFunc<(Mut,), 1, X1, R, F1> + SendSync + 'static, set_fn: impl RhaiNativeFunc<(Mut, R), 2, X2, (), F2> + SendSync + 'static, ) -> &mut Self { - self.engine.register_get_set(name, get_fn, set_fn); - self - } + let hash_1 = FuncRegistration::new_getter(&name) + .register_into_engine(self.engine, get_fn) + .hash; + let hash_2 = FuncRegistration::new_setter(&name) + .register_into_engine(self.engine, set_fn) + .hash; + self.hashes = Some(vec![hash_1, hash_2]); - /// Short-hand for registering both getter and setter functions with comments. - /// Available with the metadata feature only. - /// - /// All function signatures must start with `&mut self` and not `&self`. - /// - /// Not available under `no_object`. - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn with_get_set_and_comments< - const X1: bool, - const X2: bool, - R: Variant + Clone, - const F1: bool, - const F2: bool, - >( - &mut self, - name: impl AsRef, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut,), 1, X1, R, F1> + SendSync + 'static, - set_fn: impl RhaiNativeFunc<(Mut, R), 2, X2, (), F2> + SendSync + 'static, - ) -> &mut Self { - self.with_get_and_comments(&name, comments, get_fn) - .with_set_and_comments(&name, comments, set_fn) + self } } @@ -315,31 +288,9 @@ impl TypeBuilder<'_, T> { &mut self, get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X, R, F> + SendSync + 'static, ) -> &mut Self { - self.engine.register_indexer_get(get_fn); - self - } - - /// Register an index getter with comments. - /// Available with the metadata feature only. - /// - /// The function signature must start with `&mut self` and not `&self`. - /// - /// Not available under both `no_index` and `no_object`. - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn with_indexer_get_and_comments< - IDX: Variant + Clone, - const X: bool, - R: Variant + Clone, - const F: bool, - >( - &mut self, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X, R, F> + SendSync + 'static, - ) -> &mut Self { - FuncRegistration::new_index_getter() - .with_comments(comments) - .register_into_engine(self.engine, get_fn); + let FuncMetadata { hash, .. } = + FuncRegistration::new_index_getter().register_into_engine(self.engine, get_fn); + self.hashes = Some(vec![*hash]); self } @@ -357,30 +308,10 @@ impl TypeBuilder<'_, T> { &mut self, set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X, (), F> + SendSync + 'static, ) -> &mut Self { - self.engine.register_indexer_set(set_fn); - self - } + let FuncMetadata { hash, .. } = + FuncRegistration::new_index_setter().register_into_engine(self.engine, set_fn); + self.hashes = Some(vec![*hash]); - /// Register an index setter with comments. - /// Available with the metadata feature only. - /// - /// Not available under both `no_index` and `no_object`. - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn with_indexer_set_and_comments< - IDX: Variant + Clone, - const X: bool, - R: Variant + Clone, - const F: bool, - S: AsRef, - >( - &mut self, - comments: impl IntoIterator, - set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X, (), F> + SendSync + 'static, - ) -> &mut Self { - FuncRegistration::new_index_setter() - .with_comments(comments) - .register_into_engine(self.engine, set_fn); self } @@ -400,30 +331,14 @@ impl TypeBuilder<'_, T> { get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X1, R, F1> + SendSync + 'static, set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X2, (), F2> + SendSync + 'static, ) -> &mut Self { - self.engine.register_indexer_get_set(get_fn, set_fn); - self - } + let hash_1 = FuncRegistration::new_index_getter() + .register_into_engine(self.engine, get_fn) + .hash; + let hash_2 = FuncRegistration::new_index_setter() + .register_into_engine(self.engine, set_fn) + .hash; + self.hashes = Some(vec![hash_1, hash_2]); - /// Short-hand for registering both index getter and setter functions with comments. - /// Available with the metadata feature only. - /// - /// Not available under both `no_index` and `no_object`. - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn with_indexer_get_set_and_comments< - IDX: Variant + Clone, - const X1: bool, - const X2: bool, - R: Variant + Clone, - const F1: bool, - const F2: bool, - >( - &mut self, - comments: &[&str], - get_fn: impl RhaiNativeFunc<(Mut, IDX), 2, X1, R, F1> + SendSync + 'static, - set_fn: impl RhaiNativeFunc<(Mut, IDX, R), 3, X2, (), F2> + SendSync + 'static, - ) -> &mut Self { - self.with_indexer_get_and_comments(comments, get_fn) - .with_indexer_set_and_comments(comments, set_fn) + self } } diff --git a/src/module/mod.rs b/src/module/mod.rs index 1478e76d5..71f9a50e2 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -550,6 +550,13 @@ impl FuncRegistration { &entry.1 } + + /// Get the hash value of the function. + #[cfg(feature = "metadata")] + #[must_use] + pub fn hash(&self) -> u64 { + self.metadata.hash + } } bitflags! { @@ -1452,6 +1459,22 @@ impl Module { self } + /// _(metadata)_ Update the comments of a registered function. + /// Exported under the `metadata` feature only. + #[cfg(feature = "metadata")] + #[inline] + pub(crate) fn update_fn_comments>( + &mut self, + hash_fn: u64, + comments: impl IntoIterator, + ) -> &mut Self { + if let Some((_, f)) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) { + f.comments = comments.into_iter().map(|s| s.as_ref().into()).collect(); + } + + self + } + /// Remap type ID. #[inline] #[must_use] From 0ebcd402807d69cf60a7e0849a5371c84aefaa33 Mon Sep 17 00:00:00 2001 From: ltabis Date: Fri, 22 Mar 2024 15:12:30 +0100 Subject: [PATCH 10/15] refactor: use with_comments function to comment a custom type to reflect the function registration api --- codegen/src/custom_type.rs | 10 ++++++---- codegen/src/test/custom_type.rs | 4 ++-- src/api/build_type.rs | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/codegen/src/custom_type.rs b/codegen/src/custom_type.rs index bb909cc5c..962c8c2ef 100644 --- a/codegen/src/custom_type.rs +++ b/codegen/src/custom_type.rs @@ -113,6 +113,10 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream { }; let register = { + let method = { + quote! { builder.with_name(#display_name) } + }; + #[cfg(feature = "metadata")] { let Ok(docs) = crate::attrs::doc_attributes(&input.attrs) else { @@ -121,12 +125,10 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream { }; // Not sure how to make a Vec a literal, using a string instead. let docs = proc_macro2::Literal::string(&docs.join("\n")); - quote! { builder.with_name_and_comments(#display_name, &#docs.lines().collect::>()[..]); } + quote! { #method.with_comments(&#docs.lines().collect::>()[..]); } } #[cfg(not(feature = "metadata"))] - { - quote! { builder.with_name(#display_name); } - } + quote! { #method; } }; quote! { diff --git a/codegen/src/test/custom_type.rs b/codegen/src/test/custom_type.rs index 8fededbdf..e18e36bc0 100644 --- a/codegen/src/test/custom_type.rs +++ b/codegen/src/test/custom_type.rs @@ -118,7 +118,7 @@ mod custom_type_tests { let expected = quote! { impl CustomType for Bar { fn build(mut builder: TypeBuilder) { - builder.with_name_and_comments(stringify!(Bar), &"/// Bar comments.".lines().collect::>()[..]); + builder.with_name(stringify!(Bar)).with_comments(&"/// Bar comments.".lines().collect::>()[..]); builder.with_get_set("field1", |obj: &mut Self| obj.1.clone(), |obj: &mut Self, val| obj.1 = val @@ -163,7 +163,7 @@ mod custom_type_tests { let expected = quote! { impl CustomType for Foo { fn build(mut builder: TypeBuilder) { - builder.with_name_and_comments("MyFoo", &"/// Foo comments.".lines().collect::>()[..]); + builder.with_name("MyFoo").with_comments(&"/// Foo comments.".lines().collect::>()[..]); builder.with_get_set(stringify!(bar), |obj: &mut Self| get_bar(&*obj), |obj: &mut Self, val| obj.bar = val diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 24e5b3926..89e2784e1 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -128,7 +128,27 @@ impl TypeBuilder<'_, T> { self } + /// Set a comments for the type. + /// `TypeBuilder::with_name` must be called before this function, otherwise + /// the comments will not be registered. + /// + /// Available with the metadata feature only. + #[cfg(feature = "metadata")] + #[inline(always)] + pub fn with_comments(&mut self, comments: &[&str]) -> &mut Self { + let namespace = self.engine.global_namespace_mut(); + if let Some(name) = namespace + .get_custom_type_raw::() + .map(|ty| ty.display_name.clone()) + { + namespace.set_custom_type_with_comments::(name.as_str(), comments); + } + + self + } + /// Set a pretty-print name for the `type_of` function and comments. + /// /// Available with the metadata feature only. #[cfg(feature = "metadata")] #[inline(always)] From 26976f9fe279a54fa2828c4438a8e200df3540f8 Mon Sep 17 00:00:00 2001 From: ltabis Date: Fri, 22 Mar 2024 15:19:16 +0100 Subject: [PATCH 11/15] chore: remove unused functions --- src/api/register.rs | 54 --------------------------------------------- src/module/mod.rs | 7 ------ 2 files changed, 61 deletions(-) diff --git a/src/api/register.rs b/src/api/register.rs index 7b88b9abb..65ad5033b 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -244,60 +244,6 @@ impl Engine { self } /// Register a custom type for use with the [`Engine`], with a pretty-print name - /// for the `type_of` function and comments. The type must implement [`Clone`], - /// and the "metadata" feature must be enabled. - /// - /// # Example - /// - /// ``` - /// #[derive(Clone)] - /// struct TestStruct { - /// field: i64 - /// } - /// - /// impl TestStruct { - /// fn new() -> Self { - /// Self { field: 1 } - /// } - /// } - /// - /// # fn main() -> Result<(), Box> { - /// use rhai::Engine; - /// - /// let mut engine = Engine::new(); - /// - /// // Register API for the custom type. - /// engine - /// .register_type::() - /// .register_fn("new_ts", TestStruct::new); - /// - /// assert_eq!( - /// engine.eval::("let x = new_ts(); type_of(x)")?, - /// "rust_out::TestStruct" - /// ); - /// - /// // Re-register the custom type with a name and comments. - /// engine.register_type_with_name_and_comments::("Hello", &["A comment for this type"]); - /// - /// assert_eq!( - /// engine.eval::("let x = new_ts(); type_of(x)")?, - /// "Hello" - /// ); - /// # Ok(()) - /// # } - /// ``` - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn register_type_with_name_and_comments( - &mut self, - name: &str, - comments: &[&str], - ) -> &mut Self { - self.global_namespace_mut() - .set_custom_type_with_comments::(name, comments); - self - } - /// Register a custom type for use with the [`Engine`], with a pretty-print name /// for the `type_of` function. The type must implement [`Clone`]. /// /// # WARNING - Low Level API diff --git a/src/module/mod.rs b/src/module/mod.rs index 71f9a50e2..8131c8e6b 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -550,13 +550,6 @@ impl FuncRegistration { &entry.1 } - - /// Get the hash value of the function. - #[cfg(feature = "metadata")] - #[must_use] - pub fn hash(&self) -> u64 { - self.metadata.hash - } } bitflags! { From cef25cf0dfca2f77cb13067815006ef53467a0b1 Mon Sep 17 00:00:00 2001 From: ltabis Date: Fri, 22 Mar 2024 15:21:50 +0100 Subject: [PATCH 12/15] chore: remove TypeBuilder old comment api --- src/api/build_type.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 89e2784e1..bf08277ad 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -147,17 +147,6 @@ impl TypeBuilder<'_, T> { self } - /// Set a pretty-print name for the `type_of` function and comments. - /// - /// Available with the metadata feature only. - #[cfg(feature = "metadata")] - #[inline(always)] - pub fn with_name_and_comments(&mut self, name: &str, comments: &[&str]) -> &mut Self { - self.engine - .register_type_with_name_and_comments::(name, comments); - self - } - /// Pretty-print this custom type. #[inline(always)] pub fn on_print( From 6be770cd8c33b69adef54df17488159cc6f17e9f Mon Sep 17 00:00:00 2001 From: ltabis Date: Fri, 22 Mar 2024 18:11:47 +0100 Subject: [PATCH 13/15] refactor: move metadata registration for function directly into FuncRegistration --- src/api/register.rs | 23 +---------------------- src/module/mod.rs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/api/register.rs b/src/api/register.rs index 65ad5033b..aabe26fdb 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -74,28 +74,7 @@ impl Engine { name: impl AsRef + Into, func: FUNC, ) -> &mut Self { - let reg = FuncRegistration::new(name.into()).in_global_namespace(); - - #[cfg(feature = "metadata")] - let reg = { - let mut param_type_names = FUNC::param_names() - .iter() - .map(|ty| format!("_: {}", self.format_param_type(ty))) - .collect::>(); - - if FUNC::return_type() != TypeId::of::<()>() { - param_type_names.push(self.format_param_type(FUNC::return_type_name()).into()); - } - - let param_type_names = param_type_names - .iter() - .map(String::as_str) - .collect::>(); - - reg.with_params_info(param_type_names) - }; - - reg.set_into_module(self.global_namespace_mut(), func); + FuncRegistration::new(name.into()).register_into_engine(self, func); self } diff --git a/src/module/mod.rs b/src/module/mod.rs index 8131c8e6b..e472000a4 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -398,6 +398,27 @@ impl FuncRegistration { R: Variant + Clone, FUNC: RhaiNativeFunc + SendSync + 'static, { + #[cfg(feature = "metadata")] + { + let mut param_type_names = FUNC::param_names() + .iter() + .map(|ty| format!("_: {}", engine.format_param_type(ty))) + .collect::>(); + + if FUNC::return_type() != TypeId::of::<()>() { + param_type_names.push(engine.format_param_type(FUNC::return_type_name()).into()); + } + + let param_type_names = param_type_names + .iter() + .map(String::as_str) + .collect::>(); + + self.with_params_info(param_type_names) + .in_global_namespace() + .set_into_module(engine.global_namespace_mut(), func) + } + #[cfg(not(feature = "metadata"))] self.in_global_namespace() .set_into_module(engine.global_namespace_mut(), func) } From 55da3b128be4261161fe249a0f563ecc3731dca0 Mon Sep 17 00:00:00 2001 From: ltabis Date: Sun, 24 Mar 2024 20:02:08 +0100 Subject: [PATCH 14/15] refactor: remove option since we use a vec to store function hashes --- src/api/build_type.rs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/api/build_type.rs b/src/api/build_type.rs index bf08277ad..ecb5a808b 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -104,7 +104,7 @@ impl Engine { pub struct TypeBuilder<'a, T: Variant + Clone> { engine: &'a mut Engine, /// Keep the latest registered function(s) in cache to add additional metadata. - hashes: Option>, + hashes: Vec, _marker: PhantomData, } @@ -114,7 +114,7 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> { fn new(engine: &'a mut Engine) -> Self { Self { engine, - hashes: None, + hashes: vec![], _marker: PhantomData, } } @@ -155,7 +155,7 @@ impl TypeBuilder<'_, T> { ) -> &mut Self { let FuncMetadata { hash, .. } = FuncRegistration::new(FUNC_TO_STRING).register_into_engine(self.engine, on_print); - self.hashes = Some(vec![*hash]); + self.hashes = vec![*hash]; self } @@ -167,7 +167,7 @@ impl TypeBuilder<'_, T> { ) -> &mut Self { let FuncMetadata { hash, .. } = FuncRegistration::new(FUNC_TO_DEBUG).register_into_engine(self.engine, on_debug); - self.hashes = Some(vec![*hash]); + self.hashes = vec![*hash]; self } @@ -180,7 +180,7 @@ impl TypeBuilder<'_, T> { ) -> &mut Self { let FuncMetadata { hash, .. } = FuncRegistration::new(name).register_into_engine(self.engine, method); - self.hashes = Some(vec![*hash]); + self.hashes = vec![*hash]; self } @@ -189,13 +189,12 @@ impl TypeBuilder<'_, T> { #[cfg(feature = "metadata")] #[inline(always)] pub fn and_comments(&mut self, comments: &[&str]) -> &mut Self { - if let Some(hashes) = &self.hashes { - let module = self.engine.global_namespace_mut(); + let module = self.engine.global_namespace_mut(); - for hash in hashes { - module.update_fn_comments(*hash, comments); - } + for hash in &self.hashes { + module.update_fn_comments(*hash, comments); } + self } } @@ -229,7 +228,7 @@ impl TypeBuilder<'_, T> { ) -> &mut Self { let FuncMetadata { hash, .. } = FuncRegistration::new_getter(name).register_into_engine(self.engine, get_fn); - self.hashes = Some(vec![*hash]); + self.hashes = vec![*hash]; self } @@ -245,7 +244,7 @@ impl TypeBuilder<'_, T> { ) -> &mut Self { let FuncMetadata { hash, .. } = FuncRegistration::new_setter(name).register_into_engine(self.engine, set_fn); - self.hashes = Some(vec![*hash]); + self.hashes = vec![*hash]; self } @@ -274,7 +273,7 @@ impl TypeBuilder<'_, T> { let hash_2 = FuncRegistration::new_setter(&name) .register_into_engine(self.engine, set_fn) .hash; - self.hashes = Some(vec![hash_1, hash_2]); + self.hashes = vec![hash_1, hash_2]; self } @@ -299,7 +298,7 @@ impl TypeBuilder<'_, T> { ) -> &mut Self { let FuncMetadata { hash, .. } = FuncRegistration::new_index_getter().register_into_engine(self.engine, get_fn); - self.hashes = Some(vec![*hash]); + self.hashes = vec![*hash]; self } @@ -319,7 +318,7 @@ impl TypeBuilder<'_, T> { ) -> &mut Self { let FuncMetadata { hash, .. } = FuncRegistration::new_index_setter().register_into_engine(self.engine, set_fn); - self.hashes = Some(vec![*hash]); + self.hashes = vec![*hash]; self } @@ -346,7 +345,7 @@ impl TypeBuilder<'_, T> { let hash_2 = FuncRegistration::new_index_setter() .register_into_engine(self.engine, set_fn) .hash; - self.hashes = Some(vec![hash_1, hash_2]); + self.hashes = vec![hash_1, hash_2]; self } From 208f4cb6ef4bc4318eb6777b0bcdbfa7acf5082d Mon Sep 17 00:00:00 2001 From: ltabis Date: Sun, 24 Mar 2024 20:11:29 +0100 Subject: [PATCH 15/15] fix: update param info if they do not exist --- src/module/mod.rs | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/module/mod.rs b/src/module/mod.rs index e472000a4..44c8c42bd 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -400,24 +400,34 @@ impl FuncRegistration { { #[cfg(feature = "metadata")] { - let mut param_type_names = FUNC::param_names() - .iter() - .map(|ty| format!("_: {}", engine.format_param_type(ty))) - .collect::>(); - - if FUNC::return_type() != TypeId::of::<()>() { - param_type_names.push(engine.format_param_type(FUNC::return_type_name()).into()); - } + // Do not update parameter informations if `with_params_info` was called previously. + if self.metadata.params_info.is_empty() { + let mut param_type_names = FUNC::param_names() + .iter() + .map(|ty| format!("_: {}", engine.format_param_type(ty))) + .collect::>(); + + if FUNC::return_type() != TypeId::of::<()>() { + param_type_names + .push(engine.format_param_type(FUNC::return_type_name()).into()); + } - let param_type_names = param_type_names - .iter() - .map(String::as_str) - .collect::>(); + let param_type_names = param_type_names + .iter() + .map(String::as_str) + .collect::>(); - self.with_params_info(param_type_names) - .in_global_namespace() - .set_into_module(engine.global_namespace_mut(), func) + self.with_params_info(param_type_names) + } else { + self + } + // Duplicate of code without metadata feature because it would + // require to set self as mut, which would trigger a warning without + // the metadata feature. + .in_global_namespace() + .set_into_module(engine.global_namespace_mut(), func) } + #[cfg(not(feature = "metadata"))] self.in_global_namespace() .set_into_module(engine.global_namespace_mut(), func)