From d4f67741c4b20b8caee239a80a22cc6d698ddf38 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Thu, 5 Oct 2023 13:26:11 +0200 Subject: [PATCH] Change function registration to use builders (#639) --- crates/rune-core/src/item/into_component.rs | 35 + crates/rune-macros/src/function.rs | 21 +- crates/rune-modules/src/fs.rs | 2 +- crates/rune-modules/src/rand.rs | 13 +- crates/rune-wasm/src/http.rs | 2 +- crates/rune-wasm/src/time.rs | 6 +- crates/rune/src/compile/context.rs | 103 +-- crates/rune/src/module.rs | 42 +- crates/rune/src/module/function_meta.rs | 211 ++++-- crates/rune/src/module/module.rs | 682 ++++++++++++++---- crates/rune/src/modules/capture_io.rs | 38 +- crates/rune/src/modules/disable_io.rs | 18 +- crates/rune/src/modules/f64.rs | 22 +- crates/rune/src/modules/future.rs | 3 +- crates/rune/src/modules/i64.rs | 6 +- crates/rune/src/modules/io.rs | 2 +- crates/rune/src/modules/result.rs | 2 +- crates/rune/src/params.rs | 14 +- crates/rune/src/runtime/protocol.rs | 8 +- crates/rune/src/tests/bug_344.rs | 4 +- crates/rune/src/tests/reference_error.rs | 2 +- crates/rune/src/tests/tuple.rs | 14 +- crates/rune/src/tests/type_name_native.rs | 2 +- crates/rune/src/tests/vm_function_pointers.rs | 2 +- examples/examples/native_function.rs | 2 +- examples/examples/vec_args.rs | 5 +- 26 files changed, 919 insertions(+), 342 deletions(-) diff --git a/crates/rune-core/src/item/into_component.rs b/crates/rune-core/src/item/into_component.rs index 075bd5133..9462e54a7 100644 --- a/crates/rune-core/src/item/into_component.rs +++ b/crates/rune-core/src/item/into_component.rs @@ -45,6 +45,41 @@ pub trait IntoComponent: Sized { } } +/// IntoCompoment implementation preserved for backwards compatibility. +impl IntoComponent for [T; 1] +where + T: IntoComponent, +{ + fn as_component_ref(&self) -> ComponentRef<'_> { + let [this] = self; + this.as_component_ref() + } + + #[inline] + #[cfg(feature = "alloc")] + fn into_component(self) -> alloc::Result { + let [this] = self; + this.into_component() + } + + #[inline] + #[doc(hidden)] + #[cfg(feature = "alloc")] + fn write_component(self, output: &mut Vec) -> alloc::Result<()> { + let [this] = self; + this.write_component(output) + } + + #[inline] + fn hash_component(self, hasher: &mut H) + where + H: hash::Hasher, + { + let [this] = self; + this.hash_component(hasher) + } +} + impl IntoComponent for ComponentRef<'_> { #[inline] fn as_component_ref(&self) -> ComponentRef<'_> { diff --git a/crates/rune-macros/src/function.rs b/crates/rune-macros/src/function.rs index ebbcaaa88..79a73ebc7 100644 --- a/crates/rune-macros/src/function.rs +++ b/crates/rune-macros/src/function.rs @@ -230,10 +230,8 @@ impl Function { let name_string = syn::LitStr::new(&self.sig.ident.to_string(), self.sig.ident.span()); - let mut name; - - if instance { - name = 'out: { + let name = if instance { + 'out: { syn::Expr::Lit(syn::ExprLit { attrs: Vec::new(), lit: syn::Lit::Str(match &attrs.path { @@ -250,9 +248,9 @@ impl Function { } }), }) - }; + } } else { - name = match &attrs.path { + match &attrs.path { Path::None => expr_lit(&self.sig.ident), Path::Rename(last) => expr_lit(&last.ident), Path::Protocol(protocol) => syn::Expr::Path(syn::ExprPath { @@ -260,17 +258,6 @@ impl Function { qself: None, path: protocol.clone(), }), - }; - - if attrs.self_type.is_none() { - let mut out = syn::ExprArray { - attrs: Vec::new(), - bracket_token: syn::token::Bracket::default(), - elems: Punctuated::default(), - }; - - out.elems.push(name); - name = syn::Expr::Array(out); } }; diff --git a/crates/rune-modules/src/fs.rs b/crates/rune-modules/src/fs.rs index 5e7a3f9fb..76d30bd28 100644 --- a/crates/rune-modules/src/fs.rs +++ b/crates/rune-modules/src/fs.rs @@ -34,7 +34,7 @@ use rune::{Module, ContextError}; /// Construct the `fs` module. pub fn module(_stdio: bool) -> Result { let mut module = Module::with_crate("fs")?; - module.function(["read_to_string"], read_to_string)?; + module.function("read_to_string", read_to_string).build()?; Ok(module) } diff --git a/crates/rune-modules/src/rand.rs b/crates/rune-modules/src/rand.rs index 1d712f545..2aab5e691 100644 --- a/crates/rune-modules/src/rand.rs +++ b/crates/rune-modules/src/rand.rs @@ -40,20 +40,19 @@ pub fn module(_stdio: bool) -> Result { let mut module = Module::with_crate("rand")?; module.ty::()?; - module.function(["WyRand", "new"], WyRand::new)?; - module.function(["WyRand", "new_seed"], WyRand::new_seed)?; + module.function("new", WyRand::new).build_associated::()?; + module.function("new_seed", WyRand::new_seed).build_associated::()?; module.associated_function("int", WyRand::int)?; module.associated_function("int_range", WyRand::int_range)?; module.ty::()?; - module.function(["Pcg64", "new"], Pcg64::new)?; - module.function(["Pcg64", "new_seed"], Pcg64::new_seed)?; + module.function("new", Pcg64::new).build_associated::()?; + module.function("new_seed", Pcg64::new_seed).build_associated::()?; module.associated_function("int", Pcg64::int)?; module.associated_function("int_range", Pcg64::int_range)?; - module.function(["int"], int)?; - module.function(["int_range"], int_range)?; - + module.function("int", int).build()?; + module.function("int_range", int_range).build()?; Ok(module) } diff --git a/crates/rune-wasm/src/http.rs b/crates/rune-wasm/src/http.rs index 26775dd91..d7ca7b645 100644 --- a/crates/rune-wasm/src/http.rs +++ b/crates/rune-wasm/src/http.rs @@ -8,7 +8,7 @@ pub fn module() -> Result { let mut module = Module::with_crate("http")?; module.ty::()?; module.ty::()?; - module.function(["get"], get)?; + module.function("get", get).build()?; module.associated_function("text", Response::text)?; Ok(module) } diff --git a/crates/rune-wasm/src/time.rs b/crates/rune-wasm/src/time.rs index d42610962..8ebd3c4e5 100644 --- a/crates/rune-wasm/src/time.rs +++ b/crates/rune-wasm/src/time.rs @@ -13,8 +13,10 @@ extern "C" { pub fn module() -> Result { let mut module = Module::with_crate("time")?; module.ty::()?; - module.function(["Duration", "from_secs"], Duration::from_secs)?; - module.function(["delay_for"], delay_for)?; + module + .function("from_secs", Duration::from_secs) + .build_associated::()?; + module.function("delay_for", delay_for).build()?; Ok(module) } diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index 97233bb2c..716733d79 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -13,8 +13,8 @@ use crate::compile::MetaInfo; use crate::compile::{ComponentRef, ContextError, IntoComponent, Item, ItemBuf, Names}; use crate::hash; use crate::module::{ - Fields, InternalEnum, Module, ModuleAssociated, ModuleAttributeMacro, ModuleConstant, - ModuleFunction, ModuleMacro, ModuleType, TypeSpecification, + Fields, InternalEnum, Module, ModuleAssociated, ModuleAssociatedKind, ModuleAttributeMacro, + ModuleConstant, ModuleFunction, ModuleMacro, ModuleType, TypeSpecification, }; use crate::runtime::{ AttributeMacroHandler, ConstValue, FunctionHandler, MacroHandler, Protocol, RuntimeContext, @@ -737,69 +737,86 @@ impl Context { // should not be mixed in again. let hash = assoc .name - .associated + .kind .hash(assoc.container.hash) .with_function_parameters(assoc.name.function_parameters); - let signature = meta::Signature { - #[cfg(feature = "doc")] - is_async: assoc.is_async, - #[cfg(feature = "doc")] - deprecated: assoc.deprecated.try_clone()?, - #[cfg(feature = "doc")] - args: assoc.args, - #[cfg(feature = "doc")] - return_type: assoc.return_type.as_ref().map(|f| f.hash), - #[cfg(feature = "doc")] - argument_types: assoc - .argument_types - .iter() - .map(|f| f.as_ref().map(|f| f.hash)) - .try_collect()?, - }; - // If the associated function is a named instance function - register it // under the name of the item it corresponds to unless it's a field // function. // // The other alternatives are protocol functions (which are not free) // and plain hashes. - let item = if let meta::AssociatedKind::Instance(name) = &assoc.name.associated { + let item = if let meta::AssociatedKind::Instance(name) = &assoc.name.kind { let item = info.item.extended(name.as_ref())?; let hash = Hash::type_hash(&item) .with_type_parameters(info.type_parameters) .with_function_parameters(assoc.name.function_parameters); - self.constants.try_insert( - Hash::associated_function(hash, Protocol::INTO_TYPE_NAME), - ConstValue::String(item.try_to_string()?), - )?; - - self.insert_native_fn(hash, &assoc.handler)?; - Some(item) + Some((hash, item)) } else { None }; - self.insert_native_fn(hash, &assoc.handler)?; + let kind = match &assoc.kind { + ModuleAssociatedKind::Constant(c) => { + if let Some((hash, ..)) = &item { + self.constants.try_insert(*hash, c.value.try_clone()?)?; + } + + self.constants.try_insert(hash, c.value.try_clone()?)?; + meta::Kind::Const + } + ModuleAssociatedKind::Function(f) => { + let signature = meta::Signature { + #[cfg(feature = "doc")] + is_async: f.is_async, + #[cfg(feature = "doc")] + deprecated: assoc.deprecated.try_clone()?, + #[cfg(feature = "doc")] + args: f.args, + #[cfg(feature = "doc")] + return_type: f.return_type.as_ref().map(|f| f.hash), + #[cfg(feature = "doc")] + argument_types: f + .argument_types + .iter() + .map(|f| f.as_ref().map(|f| f.hash)) + .try_collect()?, + }; + + if let Some((hash, item)) = &item { + self.constants.try_insert( + Hash::associated_function(*hash, Protocol::INTO_TYPE_NAME), + ConstValue::String(item.try_to_string()?), + )?; + + self.insert_native_fn(*hash, &f.handler)?; + } + + self.insert_native_fn(hash, &f.handler)?; + + meta::Kind::Function { + associated: Some(assoc.name.kind.try_clone()?), + signature, + is_test: false, + is_bench: false, + parameters: Hash::EMPTY + .with_type_parameters(info.type_parameters) + .with_function_parameters(assoc.name.function_parameters), + #[cfg(feature = "doc")] + container: Some(assoc.container.hash), + #[cfg(feature = "doc")] + parameter_types: assoc.name.parameter_types.try_clone()?, + } + } + }; self.install_meta(ContextMeta { hash, - item, - kind: meta::Kind::Function { - associated: Some(assoc.name.associated.try_clone()?), - signature, - is_test: false, - is_bench: false, - parameters: Hash::EMPTY - .with_type_parameters(info.type_parameters) - .with_function_parameters(assoc.name.function_parameters), - #[cfg(feature = "doc")] - container: Some(assoc.container.hash), - #[cfg(feature = "doc")] - parameter_types: assoc.name.parameter_types.try_clone()?, - }, + item: item.map(|(_, item)| item), + kind, #[cfg(feature = "doc")] docs: assoc.docs.try_clone()?, })?; diff --git a/crates/rune/src/module.rs b/crates/rune/src/module.rs index b33d393d2..2e7be6af5 100644 --- a/crates/rune/src/module.rs +++ b/crates/rune/src/module.rs @@ -24,13 +24,16 @@ use crate::runtime::{ }; use crate::Hash; -pub(crate) use self::function_meta::{AssociatedFunctionName, ToFieldFunction, ToInstance}; +pub(crate) use self::function_meta::{AssociatedName, ToFieldFunction, ToInstance}; #[doc(hidden)] pub use self::function_meta::{FunctionMetaData, FunctionMetaKind, MacroMetaData, MacroMetaKind}; pub use self::function_traits::{Async, Function, FunctionKind, InstanceFunction, Plain}; #[doc(hidden)] -pub use self::module::{Module, ModuleMeta, ModuleMetaData}; +pub use self::module::{ + Module, ModuleConstantBuilder, ModuleFunctionBuilder, ModuleMeta, ModuleMetaData, + ModuleRawFunctionBuilder, +}; /// Trait to handle the installation of auxilliary functions for a type /// installed into a module. @@ -63,12 +66,11 @@ impl InternalEnum { static_type: &'static StaticType, ) -> alloc::Result where - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, { Ok(InternalEnum { name, - base_type: ItemBuf::with_item(base_type)?, + base_type: ItemBuf::with_item([base_type])?, static_type, variants: Vec::new(), docs: Docs::EMPTY, @@ -195,6 +197,7 @@ pub(crate) struct AssociatedKey { #[derive(TryClone)] pub(crate) struct ModuleFunction { pub(crate) item: ItemBuf, + pub(crate) docs: Docs, pub(crate) handler: Arc, #[cfg(feature = "doc")] pub(crate) is_async: bool, @@ -206,26 +209,41 @@ pub(crate) struct ModuleFunction { pub(crate) return_type: Option, #[cfg(feature = "doc")] pub(crate) argument_types: Box<[Option]>, - pub(crate) docs: Docs, } #[derive(TryClone)] -pub(crate) struct ModuleAssociated { - pub(crate) container: FullTypeOf, - pub(crate) container_type_info: TypeInfo, - pub(crate) name: AssociatedFunctionName, +pub(crate) struct ModuleAssociatedConstant { + pub(crate) value: ConstValue, +} + +#[derive(TryClone)] +pub(crate) struct ModuleAssociatedFunction { pub(crate) handler: Arc, #[cfg(feature = "doc")] pub(crate) is_async: bool, #[cfg(feature = "doc")] - pub(crate) deprecated: Option>, - #[cfg(feature = "doc")] pub(crate) args: Option, #[cfg(feature = "doc")] pub(crate) return_type: Option, #[cfg(feature = "doc")] pub(crate) argument_types: Box<[Option]>, +} + +#[derive(TryClone)] +pub(crate) enum ModuleAssociatedKind { + Constant(ModuleAssociatedConstant), + Function(ModuleAssociatedFunction), +} + +#[derive(TryClone)] +pub(crate) struct ModuleAssociated { + pub(crate) container: FullTypeOf, + pub(crate) container_type_info: TypeInfo, + pub(crate) name: AssociatedName, pub(crate) docs: Docs, + #[cfg(feature = "doc")] + pub(crate) deprecated: Option>, + pub(crate) kind: ModuleAssociatedKind, } /// Handle to a macro inserted into a module. diff --git a/crates/rune/src/module/function_meta.rs b/crates/rune/src/module/function_meta.rs index c5e164219..0bda74d6c 100644 --- a/crates/rune/src/module/function_meta.rs +++ b/crates/rune/src/module/function_meta.rs @@ -8,7 +8,7 @@ use crate::alloc::prelude::*; #[cfg(feature = "doc")] use crate::alloc::Vec; use crate::alloc::{self, try_vec, Box}; -use crate::compile::{self, meta, IntoComponent, ItemBuf, Named}; +use crate::compile::{self, meta, IntoComponent, ItemBuf}; use crate::hash::Hash; use crate::macros::{MacroContext, TokenStream}; use crate::module::{AssociatedKey, Function, FunctionKind, InstanceFunction}; @@ -67,18 +67,34 @@ pub struct FunctionData { } impl FunctionData { + pub(crate) fn from_raw(item: ItemBuf, handler: Arc) -> Self { + Self { + item, + handler, + #[cfg(feature = "doc")] + is_async: false, + #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] + args: None, + #[cfg(feature = "doc")] + return_type: None, + #[cfg(feature = "doc")] + argument_types: Box::default(), + } + } + #[inline] pub(crate) fn new(name: N, f: F) -> alloc::Result where F: Function, F::Return: MaybeTypeOf, - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, A: FunctionArgs, K: FunctionKind, { Ok(Self { - item: ItemBuf::with_item(name)?, + item: ItemBuf::with_item([name])?, handler: Arc::new(move |stack, args| f.fn_call(stack, args)), #[cfg(feature = "doc")] is_async: K::is_async(), @@ -150,19 +166,19 @@ impl AttributeMacroData { #[derive(Debug, TryClone)] #[non_exhaustive] #[doc(hidden)] -pub struct AssociatedFunctionName { +pub struct AssociatedName { /// The name of the instance function. - pub associated: meta::AssociatedKind, + pub kind: meta::AssociatedKind, /// Parameters hash. pub function_parameters: Hash, #[cfg(feature = "doc")] pub parameter_types: Vec, } -impl AssociatedFunctionName { +impl AssociatedName { pub(crate) fn index(protocol: Protocol, index: usize) -> Self { Self { - associated: meta::AssociatedKind::IndexFn(protocol, index), + kind: meta::AssociatedKind::IndexFn(protocol, index), function_parameters: Hash::EMPTY, #[cfg(feature = "doc")] parameter_types: Vec::new(), @@ -174,20 +190,20 @@ impl AssociatedFunctionName { pub trait ToInstance: self::sealed::Sealed { /// Get information on the naming of the instance function. #[doc(hidden)] - fn to_instance(self) -> alloc::Result; + fn to_instance(self) -> alloc::Result; } /// Trait used to determine what can be used as an instance function name. pub trait ToFieldFunction: self::sealed::Sealed { #[doc(hidden)] - fn to_field_function(self, protocol: Protocol) -> alloc::Result; + fn to_field_function(self, protocol: Protocol) -> alloc::Result; } impl ToInstance for &'static str { #[inline] - fn to_instance(self) -> alloc::Result { - Ok(AssociatedFunctionName { - associated: meta::AssociatedKind::Instance(Cow::Borrowed(self)), + fn to_instance(self) -> alloc::Result { + Ok(AssociatedName { + kind: meta::AssociatedKind::Instance(Cow::Borrowed(self)), function_parameters: Hash::EMPTY, #[cfg(feature = "doc")] parameter_types: Vec::new(), @@ -197,9 +213,9 @@ impl ToInstance for &'static str { impl ToFieldFunction for &'static str { #[inline] - fn to_field_function(self, protocol: Protocol) -> alloc::Result { - Ok(AssociatedFunctionName { - associated: meta::AssociatedKind::FieldFn(protocol, Cow::Borrowed(self)), + fn to_field_function(self, protocol: Protocol) -> alloc::Result { + Ok(AssociatedName { + kind: meta::AssociatedKind::FieldFn(protocol, Cow::Borrowed(self)), function_parameters: Hash::EMPTY, #[cfg(feature = "doc")] parameter_types: Vec::new(), @@ -207,12 +223,52 @@ impl ToFieldFunction for &'static str { } } +/// The full naming of an associated item. +pub struct Associated { + /// The name of the associated item. + pub(crate) name: AssociatedName, + /// The container the associated item is associated with. + pub(crate) container: FullTypeOf, + /// Type info for the container the associated item is associated with. + pub(crate) container_type_info: TypeInfo, +} + +impl Associated { + /// Construct a raw associated name. + pub fn new(name: AssociatedName, container: FullTypeOf, container_type_info: TypeInfo) -> Self { + Self { + name, + container, + container_type_info, + } + } + + /// Construct an associated name from static type information. + pub fn from_type(name: AssociatedName) -> alloc::Result + where + T: TypeOf, + { + Ok(Self { + name, + container: T::type_of(), + container_type_info: T::type_info(), + }) + } + + /// Get unique key for the associated item. + pub(crate) fn as_key(&self) -> alloc::Result { + Ok(AssociatedKey { + type_hash: self.container.hash, + kind: self.name.kind.try_clone()?, + parameters: self.name.function_parameters, + }) + } +} + /// Runtime data for an associated function. pub struct AssociatedFunctionData { - pub(crate) name: AssociatedFunctionName, + pub(crate) associated: Associated, pub(crate) handler: Arc, - pub(crate) container: FullTypeOf, - pub(crate) container_type_info: TypeInfo, #[cfg(feature = "doc")] pub(crate) is_async: bool, #[cfg(feature = "doc")] @@ -226,19 +282,34 @@ pub struct AssociatedFunctionData { } impl AssociatedFunctionData { + pub(crate) fn from_raw(associated: Associated, handler: Arc) -> Self { + Self { + associated, + handler, + #[cfg(feature = "doc")] + is_async: false, + #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] + args: None, + #[cfg(feature = "doc")] + return_type: None, + #[cfg(feature = "doc")] + argument_types: Box::default(), + } + } + #[inline] - pub(crate) fn new(name: AssociatedFunctionName, f: F) -> alloc::Result + pub(crate) fn from_function(associated: Associated, f: F) -> alloc::Result where - F: InstanceFunction, + F: Function, F::Return: MaybeTypeOf, A: FunctionArgs, K: FunctionKind, { Ok(Self { - name, + associated, handler: Arc::new(move |stack, args| f.fn_call(stack, args)), - container: F::Instance::type_of(), - container_type_info: F::Instance::type_info(), #[cfg(feature = "doc")] is_async: K::is_async(), #[cfg(feature = "doc")] @@ -252,12 +323,27 @@ impl AssociatedFunctionData { }) } - /// Get associated key. - pub(crate) fn assoc_key(&self) -> alloc::Result { - Ok(AssociatedKey { - type_hash: self.container.hash, - kind: self.name.associated.try_clone()?, - parameters: self.name.function_parameters, + #[inline] + pub(crate) fn from_instance_function(name: AssociatedName, f: F) -> alloc::Result + where + F: InstanceFunction, + F::Return: MaybeTypeOf, + A: FunctionArgs, + K: FunctionKind, + { + Ok(Self { + associated: Associated::from_type::(name)?, + handler: Arc::new(move |stack, args| f.fn_call(stack, args)), + #[cfg(feature = "doc")] + is_async: K::is_async(), + #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] + args: Some(F::args()), + #[cfg(feature = "doc")] + return_type: F::Return::maybe_type_of(), + #[cfg(feature = "doc")] + argument_types: A::into_box()?, }) } } @@ -284,11 +370,7 @@ impl FunctionMetaKind { A: FunctionArgs, K: FunctionKind, { - Ok(FunctionBuilder { - name, - f, - _marker: PhantomData, - }) + Ok(FunctionBuilder::new(name, f)) } #[doc(hidden)] @@ -301,10 +383,9 @@ impl FunctionMetaKind { A: FunctionArgs, K: FunctionKind, { - Ok(Self::AssociatedFunction(AssociatedFunctionData::new( - name.to_instance()?, - f, - )?)) + Ok(Self::AssociatedFunction( + AssociatedFunctionData::from_instance_function(name.to_instance()?, f)?, + )) } } @@ -315,6 +396,16 @@ pub struct FunctionBuilder { _marker: PhantomData<(A, K)>, } +impl FunctionBuilder { + pub(crate) fn new(name: N, f: F) -> Self { + Self { + name, + f, + _marker: PhantomData, + } + } +} + impl FunctionBuilder where F: Function, @@ -326,8 +417,7 @@ where #[inline] pub fn build(self) -> alloc::Result where - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, { Ok(FunctionMetaKind::Function(FunctionData::new( self.name, self.f, @@ -339,25 +429,30 @@ where pub fn build_associated(self) -> alloc::Result where N: ToInstance, - T: TypeOf + Named, + T: TypeOf, + { + let associated = Associated::from_type::(self.name.to_instance()?)?; + + Ok(FunctionMetaKind::AssociatedFunction( + AssociatedFunctionData::from_function(associated, self.f)?, + )) + } + + #[doc(hidden)] + #[inline] + pub fn build_associated_with( + self, + container: FullTypeOf, + container_type_info: TypeInfo, + ) -> alloc::Result + where + N: ToInstance, { + let name = self.name.to_instance()?; + let associated = Associated::new(name, container, container_type_info); + Ok(FunctionMetaKind::AssociatedFunction( - AssociatedFunctionData { - name: self.name.to_instance()?, - handler: Arc::new(move |stack, args| self.f.fn_call(stack, args)), - container: T::type_of(), - container_type_info: T::type_info(), - #[cfg(feature = "doc")] - is_async: K::is_async(), - #[cfg(feature = "doc")] - deprecated: None, - #[cfg(feature = "doc")] - args: Some(F::args()), - #[cfg(feature = "doc")] - return_type: F::Return::maybe_type_of(), - #[cfg(feature = "doc")] - argument_types: A::into_box()?, - }, + AssociatedFunctionData::from_function(associated, self.f)?, )) } } diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index 37c5ccbd3..7f10fdd32 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -4,27 +4,363 @@ use ::rust_alloc::sync::Arc; use crate as rune; use crate::alloc::prelude::*; -#[cfg(feature = "doc")] -use crate::alloc::Box; use crate::alloc::{self, HashMap, HashSet, String, Vec}; use crate::compile::{self, meta, ContextError, Docs, IntoComponent, ItemBuf, Named}; use crate::macros::{MacroContext, TokenStream}; use crate::module::function_meta::{ - AssociatedFunctionData, AssociatedFunctionName, FunctionArgs, FunctionData, FunctionMeta, - FunctionMetaKind, MacroMeta, MacroMetaKind, ToFieldFunction, ToInstance, + Associated, AssociatedFunctionData, AssociatedName, FunctionArgs, FunctionBuilder, + FunctionData, FunctionMeta, FunctionMetaKind, MacroMeta, MacroMetaKind, ToFieldFunction, + ToInstance, }; use crate::module::{ AssociatedKey, Async, EnumMut, Function, FunctionKind, InstallWith, InstanceFunction, - InternalEnum, InternalEnumMut, ItemFnMut, ItemMut, ModuleAssociated, ModuleAttributeMacro, - ModuleConstant, ModuleFunction, ModuleMacro, ModuleType, Plain, TypeMut, TypeSpecification, - VariantMut, + InternalEnum, InternalEnumMut, ItemFnMut, ItemMut, ModuleAssociated, ModuleAssociatedConstant, + ModuleAssociatedFunction, ModuleAssociatedKind, ModuleAttributeMacro, ModuleConstant, + ModuleFunction, ModuleMacro, ModuleType, Plain, TypeMut, TypeSpecification, VariantMut, }; use crate::runtime::{ - AttributeMacroHandler, ConstValue, FromValue, GeneratorState, MacroHandler, MaybeTypeOf, - Protocol, Stack, ToValue, TypeCheck, TypeOf, Value, VmResult, + AttributeMacroHandler, ConstValue, FromValue, FullTypeOf, FunctionHandler, GeneratorState, + MacroHandler, MaybeTypeOf, Protocol, Stack, ToValue, TypeCheck, TypeInfo, TypeOf, Value, + VmResult, }; use crate::Hash; +/// Function builder as returned by [`Module::function`]. +/// +/// This allows for building a function regularly with +/// [`ModuleFunctionBuilder::build`] or statically associate the function with a +/// type through [`ModuleFunctionBuilder::build_associated::`]. +#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] +pub struct ModuleFunctionBuilder<'a, F, A, N, K> { + module: &'a mut Module, + inner: FunctionBuilder, +} + +impl<'a, F, A, N, K> ModuleFunctionBuilder<'a, F, A, N, K> +where + F: Function, + F::Return: MaybeTypeOf, + A: FunctionArgs, + K: FunctionKind, +{ + /// Construct a regular function. + /// + /// This register the function as a free function in the module it's + /// associated with, who's full name is the name of the module extended by + /// the name of the function. + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// + /// let mut m = Module::with_item(["module"])?; + /// m.function("floob", || ()).build()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build(self) -> Result, ContextError> + where + N: IntoComponent, + { + let meta = self.inner.build()?; + self.module.function_from_meta_kind(meta) + } + + /// Construct a function that is associated with `T`. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the type `T`. + /// + /// # Errors + /// + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install + /// + /// ``` + /// use rune::{Any, Context, Module}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.function("floob", || ()).build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.ty::()?; + /// m.function("floob", || ()).build_associated::()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build_associated(self) -> Result, ContextError> + where + N: ToInstance, + T: TypeOf, + { + let meta = self.inner.build_associated::()?; + self.module.function_from_meta_kind(meta) + } + + /// Construct a function that is associated with a custom dynamically + /// specified container. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the specified type. + /// + /// [`FullTypeOf`] and [`TypeInfo`] are usually constructed through the + /// [`TypeOf`] trait. But that requires access to a static type, for which + /// you should use [`build_associated`] instead. + /// + /// # Errors + /// + /// The function call will error if the specified type is not already + /// registered in the module. + /// + /// [`build_associated`]: ModuleFunctionBuilder::build_associated + #[inline] + pub fn build_associated_with( + self, + container: FullTypeOf, + container_type_info: TypeInfo, + ) -> Result, ContextError> + where + N: ToInstance, + { + let meta = self + .inner + .build_associated_with(container, container_type_info)?; + self.module.function_from_meta_kind(meta) + } +} + +/// Raw function builder as returned by [`Module::raw_function`]. +/// +/// This allows for building a function regularly with +/// [`ModuleRawFunctionBuilder::build`] or statically associate the function +/// with a type through [`ModuleRawFunctionBuilder::build_associated::`]. +#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] +pub struct ModuleRawFunctionBuilder<'a, N> { + module: &'a mut Module, + name: N, + handler: Arc, +} + +impl<'a, N> ModuleRawFunctionBuilder<'a, N> { + /// Construct a regular function. + /// + /// This register the function as a free function in the module it's + /// associated with, who's full name is the name of the module extended by + /// the name of the function. + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// use rune::runtime::VmResult; + /// + /// let mut m = Module::with_item(["module"])?; + /// m.raw_function("floob", |stac, args| VmResult::Ok(())).build()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build(self) -> Result, ContextError> + where + N: IntoComponent, + { + let item = ItemBuf::with_item([self.name])?; + self.module + .function_from_meta_kind(FunctionMetaKind::Function(FunctionData::from_raw( + item, + self.handler, + ))) + } + + /// Construct a function that is associated with `T`. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the type `T`. + /// + /// # Errors + /// + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install + /// + /// ``` + /// use rune::{Any, Module, Context}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.function("floob", || ()).build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// use rune::runtime::VmResult; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.ty::()?; + /// m.raw_function("floob", |_, _| VmResult::Ok(())).build_associated::()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build_associated(self) -> Result, ContextError> + where + N: ToInstance, + T: TypeOf, + { + let associated = Associated::from_type::(self.name.to_instance()?)?; + + self.module + .function_from_meta_kind(FunctionMetaKind::AssociatedFunction( + AssociatedFunctionData::from_raw(associated, self.handler), + )) + } + + /// Construct a function that is associated with a custom dynamically + /// specified container. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the specified type. + /// + /// [`FullTypeOf`] and [`TypeInfo`] are usually constructed through the + /// [`TypeOf`] trait. But that requires access to a static type, for which + /// you should use [`build_associated`] instead. + /// + /// # Errors + /// + /// The function call will error if the specified type is not already + /// registered in the module. + /// + /// [`build_associated`]: ModuleFunctionBuilder::build_associated + #[inline] + pub fn build_associated_with( + self, + container: FullTypeOf, + container_type_info: TypeInfo, + ) -> Result, ContextError> + where + N: ToInstance, + { + let associated = Associated::new(self.name.to_instance()?, container, container_type_info); + self.module + .function_from_meta_kind(FunctionMetaKind::AssociatedFunction( + AssociatedFunctionData::from_raw(associated, self.handler), + )) + } +} + +/// Raw function builder as returned by [`Module::raw_function`]. +/// +/// This allows for building a function regularly with +/// [`ModuleConstantBuilder::build`] or statically associate the function with a +/// type through [`ModuleConstantBuilder::build_associated::`]. +#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] +pub struct ModuleConstantBuilder<'a, N, V> { + module: &'a mut Module, + name: N, + value: V, +} + +impl<'a, N, V> ModuleConstantBuilder<'a, N, V> +where + V: ToValue, +{ + /// Add the free constant directly to the module. + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// use rune::runtime::VmResult; + /// + /// let mut m = Module::with_item(["module"])?; + /// m.constant("NAME", "Hello World").build()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + pub fn build(self) -> Result, ContextError> + where + N: IntoComponent, + { + let item = ItemBuf::with_item([self.name])?; + self.module.insert_constant(item, self.value) + } + + /// Build a constant that is associated with the static type `T`. + /// + /// # Errors + /// + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install + /// + /// ``` + /// use rune::{Any, Context, Module}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.constant("CONSTANT", "Hello World").build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// + /// let mut module = Module::default(); + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// module.constant("TEN", 10).build_associated::()?.docs(["Ten which is an associated constant."]); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + pub fn build_associated(self) -> Result, ContextError> + where + T: TypeOf, + N: ToInstance, + { + let name = self.name.to_instance()?; + let associated = Associated::from_type::(name)?; + self.module + .insert_associated_constant(associated, self.value) + } +} + #[doc(hidden)] pub struct ModuleMetaData { #[doc(hidden)] @@ -200,7 +536,7 @@ impl Module { /// ``` pub fn ty(&mut self) -> Result, ContextError> where - T: ?Sized + Named + TypeOf + InstallWith, + T: ?Sized + TypeOf + Named + InstallWith, { let item = ItemBuf::with_item([T::BASE_NAME])?; let hash = T::type_hash(); @@ -244,7 +580,7 @@ impl Module { /// Accessor to modify type metadata such as documentaiton, fields, variants. pub fn type_meta(&mut self) -> Result, ContextError> where - T: ?Sized + Named + TypeOf, + T: ?Sized + TypeOf + Named, { let type_hash = T::type_hash(); @@ -275,7 +611,7 @@ impl Module { #[deprecated = "Use type_meta::().make_struct(fields) instead"] pub fn struct_meta(&mut self, fields: &'static [&'static str]) -> Result<(), ContextError> where - T: Named + TypeOf, + T: ?Sized + TypeOf + Named, { self.type_meta::()?.make_named_struct(fields)?; Ok(()) @@ -290,7 +626,7 @@ impl Module { variants: &'static [&'static str], ) -> Result, ContextError> where - T: Named + TypeOf, + T: ?Sized + TypeOf + Named, { self.type_meta::()?.make_enum(variants) } @@ -298,7 +634,7 @@ impl Module { /// Access variant metadata for the given type and the index of its variant. pub fn variant_meta(&mut self, index: usize) -> Result, ContextError> where - T: Named + TypeOf, + T: ?Sized + TypeOf + Named, { let type_hash = T::type_hash(); @@ -345,7 +681,7 @@ impl Module { ) -> Result<(), ContextError> where F: Function, - F::Return: Named + TypeOf, + F::Return: TypeOf + Named, { self.variant_meta::(index)? .constructor(constructor)?; @@ -374,8 +710,7 @@ impl Module { name: N, ) -> Result, ContextError> where - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, { let mut enum_ = InternalEnum::new( "GeneratorState", @@ -423,8 +758,7 @@ impl Module { /// Ok::<_, rune::support::Error>(()) pub fn option(&mut self, name: N) -> Result>, ContextError> where - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, { let mut enum_ = InternalEnum::new("Option", name, crate::runtime::static_type::OPTION_TYPE)?; @@ -463,8 +797,7 @@ impl Module { name: N, ) -> Result>, ContextError> where - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, { let mut enum_ = InternalEnum::new("Result", name, crate::runtime::static_type::RESULT_TYPE)?; @@ -487,23 +820,32 @@ impl Module { /// # Examples /// /// ``` - /// use rune::Module; + /// use rune::{Any, Module}; /// /// let mut module = Module::default(); /// - /// module.constant(["TEN"], 10)?.docs(["A global ten value."]); - /// module.constant(["MyType", "TEN"], 10)?.docs(["Ten which looks like an associated constant."]); + /// #[derive(Any)] + /// struct MyType; + /// + /// module.constant("TEN", 10).build()?.docs(["A global ten value."]); + /// module.constant("TEN", 10).build_associated::()?.docs(["Ten which looks like an associated constant."]); /// # Ok::<_, rune::support::Error>(()) /// ``` - pub fn constant(&mut self, name: N, value: V) -> Result, ContextError> + pub fn constant(&mut self, name: N, value: V) -> ModuleConstantBuilder<'_, N, V> where - N: IntoIterator, - N::Item: IntoComponent, V: ToValue, { - let item = ItemBuf::with_item(name)?; - let hash = Hash::type_hash(&item); + ModuleConstantBuilder { + module: self, + name, + value, + } + } + fn insert_constant(&mut self, item: ItemBuf, value: V) -> Result, ContextError> + where + V: ToValue, + { let value = match value.to_value() { VmResult::Ok(v) => v, VmResult::Err(error) => return Err(ContextError::ValueError { error }), @@ -514,6 +856,8 @@ impl Module { VmResult::Err(error) => return Err(ContextError::ValueError { error }), }; + let hash = Hash::type_hash(&item); + if !self.names.try_insert(Name::Item(hash))? { return Err(ContextError::ConflictingConstantName { item, hash }); } @@ -528,6 +872,43 @@ impl Module { Ok(ItemMut { docs: &mut c.docs }) } + fn insert_associated_constant( + &mut self, + associated: Associated, + value: V, + ) -> Result, ContextError> + where + V: ToValue, + { + let value = match value.to_value() { + VmResult::Ok(v) => v, + VmResult::Err(error) => return Err(ContextError::ValueError { error }), + }; + + let value = match ::from_value(value) { + VmResult::Ok(v) => v, + VmResult::Err(error) => return Err(ContextError::ValueError { error }), + }; + + self.insert_associated_name(&associated)?; + + self.associated.try_push(ModuleAssociated { + container: associated.container, + container_type_info: associated.container_type_info, + name: associated.name, + #[cfg(feature = "doc")] + deprecated: None, + docs: Docs::EMPTY, + kind: ModuleAssociatedKind::Constant(ModuleAssociatedConstant { value }), + })?; + + let last = self.associated.last_mut().unwrap(); + + Ok(ItemMut { + docs: &mut last.docs, + }) + } + /// Register a native macro handler through its meta. /// /// The metadata must be provided by annotating the function with @@ -652,10 +1033,9 @@ impl Module { + Send + Sync + Fn(&mut MacroContext<'_, '_, '_>, &TokenStream) -> compile::Result, - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, { - let item = ItemBuf::with_item(name)?; + let item = ItemBuf::with_item([name])?; let hash = Hash::type_hash(&item); if !self.names.try_insert(Name::Macro(hash))? { @@ -713,10 +1093,9 @@ impl Module { &TokenStream, &TokenStream, ) -> compile::Result, - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, { - let item = ItemBuf::with_item(name)?; + let item = ItemBuf::with_item([name])?; let hash = Hash::type_hash(&item); if !self.names.try_insert(Name::AttributeMacro(hash))? { @@ -822,7 +1201,19 @@ impl Module { let mut docs = Docs::EMPTY; docs.set_docs(meta.docs)?; docs.set_arguments(meta.arguments)?; - self.assoc_fn(data, docs) + self.insert_associated_function(data, docs) + } + } + } + + fn function_from_meta_kind( + &mut self, + kind: FunctionMetaKind, + ) -> Result, ContextError> { + match kind { + FunctionMetaKind::Function(data) => self.function_inner(data, Docs::EMPTY), + FunctionMetaKind::AssociatedFunction(data) => { + self.insert_associated_function(data, Docs::EMPTY) } } } @@ -843,7 +1234,9 @@ impl Module { /// /// let mut module = Module::default(); /// - /// module.function(["add_ten"], add_ten)?.docs(["Adds 10 to any integer passed in."]); + /// module.function("add_ten", add_ten) + /// .build()? + /// .docs(["Adds 10 to any integer passed in."])?; /// # Ok::<_, rune::support::Error>(()) /// ``` /// @@ -864,20 +1257,40 @@ impl Module { /// /// let mut module = Module::default(); /// - /// module.function(["download_quote"], download_quote)? + /// module.function("download_quote", download_quote).build()? /// .docs(["Download a random quote from the internet."]); /// # Ok::<_, rune::support::Error>(()) /// ``` - pub fn function(&mut self, name: N, f: F) -> Result, ContextError> + pub fn function(&mut self, name: N, f: F) -> ModuleFunctionBuilder<'_, F, A, N, K> where F: Function, F::Return: MaybeTypeOf, - N: IntoIterator, - N::Item: IntoComponent, A: FunctionArgs, K: FunctionKind, { - self.function_inner(FunctionData::new(name, f)?, Docs::EMPTY) + ModuleFunctionBuilder { + module: self, + inner: FunctionBuilder::new(name, f), + } + } + + /// See [`Module::function`]. + #[deprecated = "Use `Module::function`"] + pub fn function2( + &mut self, + name: N, + f: F, + ) -> Result, ContextError> + where + F: Function, + F::Return: MaybeTypeOf, + A: FunctionArgs, + K: FunctionKind, + { + Ok(ModuleFunctionBuilder { + module: self, + inner: FunctionBuilder::new(name, f), + }) } /// See [`Module::function`]. @@ -886,8 +1299,7 @@ impl Module { where F: Function, F::Return: MaybeTypeOf, - N: IntoIterator, - N::Item: IntoComponent, + N: IntoComponent, A: FunctionArgs, { self.function_inner(FunctionData::new(name, f)?, Docs::EMPTY) @@ -1083,8 +1495,8 @@ impl Module { A: FunctionArgs, K: FunctionKind, { - self.assoc_fn( - AssociatedFunctionData::new(name.to_instance()?, f)?, + self.insert_associated_function( + AssociatedFunctionData::from_instance_function(name.to_instance()?, f)?, Docs::EMPTY, ) } @@ -1131,8 +1543,8 @@ impl Module { F::Return: MaybeTypeOf, A: FunctionArgs, { - self.assoc_fn( - AssociatedFunctionData::new(name.to_field_function(protocol)?, f)?, + self.insert_associated_function( + AssociatedFunctionData::from_instance_function(name.to_field_function(protocol)?, f)?, Docs::EMPTY, ) } @@ -1170,8 +1582,11 @@ impl Module { F::Return: MaybeTypeOf, A: FunctionArgs, { - let name = AssociatedFunctionName::index(protocol, index); - self.assoc_fn(AssociatedFunctionData::new(name, f)?, Docs::EMPTY) + let name = AssociatedName::index(protocol, index); + self.insert_associated_function( + AssociatedFunctionData::from_instance_function(name, f)?, + Docs::EMPTY, + ) } /// See [`Module::index_function`]. @@ -1217,56 +1632,33 @@ impl Module { /// /// let mut module = Module::default(); /// - /// let sum = module.raw_fn(["sum"], sum)?; - /// sum.docs([ - /// "Sum all numbers provided to the function." - /// ]); + /// module.raw_function("sum", sum) + /// .build()? + /// .docs([ + /// "Sum all numbers provided to the function." + /// ])?; + /// /// # Ok::<_, rune::support::Error>(()) /// ``` - pub fn raw_fn(&mut self, name: N, f: F) -> Result, ContextError> + pub fn raw_function(&mut self, name: N, f: F) -> ModuleRawFunctionBuilder<'_, N> where F: 'static + Fn(&mut Stack, usize) -> VmResult<()> + Send + Sync, - N: IntoIterator, - N::Item: IntoComponent, { - let item = ItemBuf::with_item(name)?; - let hash = Hash::type_hash(&item); - - if !self.names.try_insert(Name::Item(hash))? { - return Err(ContextError::ConflictingFunctionName { item, hash }); - } - - self.functions.try_push(ModuleFunction { - item, + ModuleRawFunctionBuilder { + module: self, + name, handler: Arc::new(move |stack, args| f(stack, args)), - #[cfg(feature = "doc")] - is_async: false, - #[cfg(feature = "doc")] - deprecated: None, - #[cfg(feature = "doc")] - args: None, - #[cfg(feature = "doc")] - return_type: None, - #[cfg(feature = "doc")] - argument_types: Box::default(), - docs: Docs::EMPTY, - })?; - - let last = self.functions.last_mut().unwrap(); + } + } - Ok(ItemFnMut { - docs: &mut last.docs, - #[cfg(feature = "doc")] - is_async: &mut last.is_async, - #[cfg(feature = "doc")] - deprecated: &mut last.deprecated, - #[cfg(feature = "doc")] - args: &mut last.args, - #[cfg(feature = "doc")] - return_type: &mut last.return_type, - #[cfg(feature = "doc")] - argument_types: &mut last.argument_types, - }) + /// See [`Module::raw_function`]. + #[deprecated = "Use `raw_function` builder instead"] + pub fn raw_fn(&mut self, name: N, f: F) -> Result, ContextError> + where + F: 'static + Fn(&mut Stack, usize) -> VmResult<()> + Send + Sync, + N: IntoComponent, + { + self.raw_function(name, f).build() } fn function_inner( @@ -1317,73 +1709,91 @@ impl Module { } /// Install an associated function. - fn assoc_fn( + fn insert_associated_function( &mut self, data: AssociatedFunctionData, docs: Docs, ) -> Result, ContextError> { - if !self.names.try_insert(Name::Associated(data.assoc_key()?))? { - return Err(match data.name.associated { + self.insert_associated_name(&data.associated)?; + + self.associated.try_push(ModuleAssociated { + container: data.associated.container, + container_type_info: data.associated.container_type_info, + name: data.associated.name, + #[cfg(feature = "doc")] + deprecated: data.deprecated, + docs, + kind: ModuleAssociatedKind::Function(ModuleAssociatedFunction { + handler: data.handler, + #[cfg(feature = "doc")] + is_async: data.is_async, + #[cfg(feature = "doc")] + #[cfg(feature = "doc")] + args: data.args, + #[cfg(feature = "doc")] + return_type: data.return_type, + #[cfg(feature = "doc")] + argument_types: data.argument_types, + }), + })?; + + let last = self.associated.last_mut().unwrap(); + + #[cfg(feature = "doc")] + let last_fn = match &mut last.kind { + ModuleAssociatedKind::Function(f) => f, + _ => unreachable!(), + }; + + Ok(ItemFnMut { + docs: &mut last.docs, + #[cfg(feature = "doc")] + is_async: &mut last_fn.is_async, + #[cfg(feature = "doc")] + deprecated: &mut last.deprecated, + #[cfg(feature = "doc")] + args: &mut last_fn.args, + #[cfg(feature = "doc")] + return_type: &mut last_fn.return_type, + #[cfg(feature = "doc")] + argument_types: &mut last_fn.argument_types, + }) + } + + fn insert_associated_name(&mut self, associated: &Associated) -> Result<(), ContextError> { + if !self + .names + .try_insert(Name::Associated(associated.as_key()?))? + { + return Err(match &associated.name.kind { meta::AssociatedKind::Protocol(protocol) => { ContextError::ConflictingProtocolFunction { - type_info: data.container_type_info, + type_info: associated.container_type_info.try_clone()?, name: protocol.name.try_into()?, } } meta::AssociatedKind::FieldFn(protocol, field) => { ContextError::ConflictingFieldFunction { - type_info: data.container_type_info, + type_info: associated.container_type_info.try_clone()?, name: protocol.name.try_into()?, - field: field.try_into()?, + field: field.as_ref().try_into()?, } } meta::AssociatedKind::IndexFn(protocol, index) => { ContextError::ConflictingIndexFunction { - type_info: data.container_type_info, + type_info: associated.container_type_info.try_clone()?, name: protocol.name.try_into()?, - index, + index: *index, } } meta::AssociatedKind::Instance(name) => ContextError::ConflictingInstanceFunction { - type_info: data.container_type_info, - name: name.try_into()?, + type_info: associated.container_type_info.try_clone()?, + name: name.as_ref().try_into()?, }, }); } - self.associated.try_push(ModuleAssociated { - container: data.container, - container_type_info: data.container_type_info, - name: data.name, - handler: data.handler, - #[cfg(feature = "doc")] - is_async: data.is_async, - #[cfg(feature = "doc")] - deprecated: data.deprecated, - #[cfg(feature = "doc")] - args: data.args, - #[cfg(feature = "doc")] - return_type: data.return_type, - #[cfg(feature = "doc")] - argument_types: data.argument_types, - docs, - })?; - - let last = self.associated.last_mut().unwrap(); - - Ok(ItemFnMut { - docs: &mut last.docs, - #[cfg(feature = "doc")] - is_async: &mut last.is_async, - #[cfg(feature = "doc")] - deprecated: &mut last.deprecated, - #[cfg(feature = "doc")] - args: &mut last.args, - #[cfg(feature = "doc")] - return_type: &mut last.return_type, - #[cfg(feature = "doc")] - argument_types: &mut last.argument_types, - }) + Ok(()) } } diff --git a/crates/rune/src/modules/capture_io.rs b/crates/rune/src/modules/capture_io.rs index 0d2472302..672cf0428 100644 --- a/crates/rune/src/modules/capture_io.rs +++ b/crates/rune/src/modules/capture_io.rs @@ -29,28 +29,34 @@ pub fn module(io: &CaptureIo) -> Result { let o = io.clone(); - module.function(["print"], move |m: &str| { - match write!(o.inner.lock(), "{}", m) { - Ok(()) => VmResult::Ok(()), - Err(error) => VmResult::panic(error), - } - })?; + module + .function("print", move |m: &str| { + match write!(o.inner.lock(), "{}", m) { + Ok(()) => VmResult::Ok(()), + Err(error) => VmResult::panic(error), + } + }) + .build()?; let o = io.clone(); - module.function(["println"], move |m: &str| { - match writeln!(o.inner.lock(), "{}", m) { - Ok(()) => VmResult::Ok(()), - Err(error) => VmResult::panic(error), - } - })?; + module + .function("println", move |m: &str| { + match writeln!(o.inner.lock(), "{}", m) { + Ok(()) => VmResult::Ok(()), + Err(error) => VmResult::panic(error), + } + }) + .build()?; let o = io.clone(); - module.raw_fn(["dbg"], move |stack, args| { - let mut o = o.inner.lock(); - dbg_impl(&mut o, stack, args) - })?; + module + .raw_function("dbg", move |stack, args| { + let mut o = o.inner.lock(); + dbg_impl(&mut o, stack, args) + }) + .build()?; Ok(module) } diff --git a/crates/rune/src/modules/disable_io.rs b/crates/rune/src/modules/disable_io.rs index 5c4528f93..d750c271e 100644 --- a/crates/rune/src/modules/disable_io.rs +++ b/crates/rune/src/modules/disable_io.rs @@ -16,16 +16,18 @@ use crate::{ContextError, Module}; pub fn module() -> Result { let mut module = Module::with_crate_item("std", ["io"])?; - module.function(["print"], move |_: &str| {})?; + module.function("print", move |_: &str| {}).build()?; - module.function(["println"], move |_: &str| {})?; + module.function("println", move |_: &str| {}).build()?; - module.raw_fn(["dbg"], move |stack: &mut Stack, args: usize| { - // NB: still need to maintain the stack. - drop(vm_try!(stack.drain(args))); - vm_try!(stack.push(Value::from(()))); - VmResult::Ok(()) - })?; + module + .raw_function("dbg", move |stack: &mut Stack, args: usize| { + // NB: still need to maintain the stack. + drop(vm_try!(stack.drain(args))); + vm_try!(stack.push(Value::from(()))); + VmResult::Ok(()) + }) + .build()?; Ok(module) } diff --git a/crates/rune/src/modules/f64.rs b/crates/rune/src/modules/f64.rs index 25cf0685a..fbb2d2912 100644 --- a/crates/rune/src/modules/f64.rs +++ b/crates/rune/src/modules/f64.rs @@ -32,17 +32,17 @@ pub fn module() -> Result { m.function_meta(partial_cmp)?; m.function_meta(cmp)?; - m.constant(["EPSILON"], f64::EPSILON)?; - m.constant(["MIN"], f64::MIN)?; - m.constant(["MAX"], f64::MAX)?; - m.constant(["MIN_POSITIVE"], f64::MIN_POSITIVE)?; - m.constant(["MIN_EXP"], f64::MIN_EXP)?; - m.constant(["MAX_EXP"], f64::MAX_EXP)?; - m.constant(["MIN_10_EXP"], f64::MIN_10_EXP)?; - m.constant(["MAX_10_EXP"], f64::MAX_10_EXP)?; - m.constant(["NAN"], f64::NAN)?; - m.constant(["INFINITY"], f64::INFINITY)?; - m.constant(["NEG_INFINITY"], f64::NEG_INFINITY)?; + m.constant("EPSILON", f64::EPSILON).build()?; + m.constant("MIN", f64::MIN).build()?; + m.constant("MAX", f64::MAX).build()?; + m.constant("MIN_POSITIVE", f64::MIN_POSITIVE).build()?; + m.constant("MIN_EXP", f64::MIN_EXP).build()?; + m.constant("MAX_EXP", f64::MAX_EXP).build()?; + m.constant("MIN_10_EXP", f64::MIN_10_EXP).build()?; + m.constant("MAX_10_EXP", f64::MAX_10_EXP).build()?; + m.constant("NAN", f64::NAN).build()?; + m.constant("INFINITY", f64::INFINITY).build()?; + m.constant("NEG_INFINITY", f64::NEG_INFINITY).build()?; Ok(m) } diff --git a/crates/rune/src/modules/future.rs b/crates/rune/src/modules/future.rs index beb4adeac..82ee5f9a7 100644 --- a/crates/rune/src/modules/future.rs +++ b/crates/rune/src/modules/future.rs @@ -10,7 +10,8 @@ pub fn module() -> Result { module.ty::()?; module - .raw_fn(["join"], raw_join)? + .raw_function("join", raw_join) + .build()? .is_async(true) .args(1) .argument_types([None])? diff --git a/crates/rune/src/modules/i64.rs b/crates/rune/src/modules/i64.rs index da538e30b..e7dd604aa 100644 --- a/crates/rune/src/modules/i64.rs +++ b/crates/rune/src/modules/i64.rs @@ -13,7 +13,7 @@ use crate::{ContextError, Module}; pub fn module() -> Result { let mut module = Module::with_crate_item("std", ["i64"])?; - module.function(["parse"], parse)?; + module.function("parse", parse).build()?; module.function_meta(to_float)?; module.function_meta(max)?; @@ -49,7 +49,7 @@ pub fn module() -> Result { module.function_meta(cmp)?; module.function_meta(to_string)?; - module.constant(["MIN"], i64::MIN)?.docs([ + module.constant("MIN", i64::MIN).build()?.docs([ "The smallest value that can be represented by this integer type", "(−263).", "", @@ -62,7 +62,7 @@ pub fn module() -> Result { "```", ])?; - module.constant(["MAX"], i64::MAX)?.docs([ + module.constant("MAX", i64::MAX).build()?.docs([ "The largest value that can be represented by this integer type", "(263 − 1).", "", diff --git a/crates/rune/src/modules/io.rs b/crates/rune/src/modules/io.rs index 9e5e7996a..b4057295f 100644 --- a/crates/rune/src/modules/io.rs +++ b/crates/rune/src/modules/io.rs @@ -37,7 +37,7 @@ pub fn module(stdio: bool) -> Result { module.function_meta(print_impl)?; module.function_meta(println_impl)?; - module.raw_fn(["dbg"], dbg_impl)?.docs([ + module.raw_function("dbg", dbg_impl).build()?.docs([ "Debug to output.", "", "This is the actual output hook, and if you install rune modules without", diff --git a/crates/rune/src/modules/result.rs b/crates/rune/src/modules/result.rs index 9acea4646..4ee7cc253 100644 --- a/crates/rune/src/modules/result.rs +++ b/crates/rune/src/modules/result.rs @@ -11,7 +11,7 @@ pub fn module() -> Result { let mut module = Module::with_crate_item("std", ["result"])?; // Sorted for ease of finding let mut result = module - .result(["Result"])? + .result("Result")? .static_docs(&["Result is a type that represents either success (Ok) or failure (Err)."])?; result diff --git a/crates/rune/src/params.rs b/crates/rune/src/params.rs index 39c4a78c0..38c38c52b 100644 --- a/crates/rune/src/params.rs +++ b/crates/rune/src/params.rs @@ -2,7 +2,7 @@ use crate::alloc; #[cfg(feature = "doc")] use crate::alloc::prelude::*; use crate::hash::Hash; -use crate::module::{AssociatedFunctionName, ToFieldFunction, ToInstance}; +use crate::module::{AssociatedName, ToFieldFunction, ToInstance}; use crate::runtime::Protocol; #[doc(inline)] @@ -13,11 +13,11 @@ where T: ToInstance, { #[inline] - fn to_instance(self) -> alloc::Result { + fn to_instance(self) -> alloc::Result { let info = self.name.to_instance()?; - Ok(AssociatedFunctionName { - associated: info.associated, + Ok(AssociatedName { + kind: info.kind, function_parameters: Hash::parameters(self.parameters.iter().map(|t| t.hash)), #[cfg(feature = "doc")] parameter_types: self.parameters.iter().map(|t| t.hash).try_collect()?, @@ -30,11 +30,11 @@ where T: ToFieldFunction, { #[inline] - fn to_field_function(self, protocol: Protocol) -> alloc::Result { + fn to_field_function(self, protocol: Protocol) -> alloc::Result { let info = self.name.to_field_function(protocol)?; - Ok(AssociatedFunctionName { - associated: info.associated, + Ok(AssociatedName { + kind: info.kind, function_parameters: Hash::parameters(self.parameters.iter().map(|p| p.hash)), #[cfg(feature = "doc")] parameter_types: self.parameters.iter().map(|p| p.hash).try_collect()?, diff --git a/crates/rune/src/runtime/protocol.rs b/crates/rune/src/runtime/protocol.rs index 858e585df..a6dedcc3a 100644 --- a/crates/rune/src/runtime/protocol.rs +++ b/crates/rune/src/runtime/protocol.rs @@ -2,7 +2,7 @@ use crate::alloc; #[cfg(feature = "doc")] use crate::alloc::Vec; use crate::compile::meta; -use crate::module::{AssociatedFunctionName, ToInstance}; +use crate::module::{AssociatedName, ToInstance}; use crate::Hash; #[doc(inline)] @@ -10,9 +10,9 @@ pub use rune_core::Protocol; impl ToInstance for Protocol { #[inline] - fn to_instance(self) -> alloc::Result { - Ok(AssociatedFunctionName { - associated: meta::AssociatedKind::Protocol(self), + fn to_instance(self) -> alloc::Result { + Ok(AssociatedName { + kind: meta::AssociatedKind::Protocol(self), function_parameters: Hash::EMPTY, #[cfg(feature = "doc")] parameter_types: Vec::new(), diff --git a/crates/rune/src/tests/bug_344.rs b/crates/rune/src/tests/bug_344.rs index 29f5f25ec..aa8406019 100644 --- a/crates/rune/src/tests/bug_344.rs +++ b/crates/rune/src/tests/bug_344.rs @@ -17,7 +17,7 @@ fn bug_344_function() -> Result<()> { let mut context = Context::new(); let mut module = Module::new(); - module.function(["function"], function)?; + module.function("function", function).build()?; context.install(module)?; let runtime = context.runtime()?; @@ -74,7 +74,7 @@ fn bug_344_async_function() -> Result<()> { let mut context = Context::new(); let mut module = Module::new(); - module.function(["function"], function)?; + module.function("function", function).build()?; context.install(module)?; let runtime = context.runtime()?; diff --git a/crates/rune/src/tests/reference_error.rs b/crates/rune/src/tests/reference_error.rs index cf8c3b265..70ac9173d 100644 --- a/crates/rune/src/tests/reference_error.rs +++ b/crates/rune/src/tests/reference_error.rs @@ -16,7 +16,7 @@ fn test_reference_error() -> Result<()> { } let mut module = Module::new(); - module.function(["take_it"], take_it)?; + module.function("take_it", take_it).build()?; let mut context = Context::new(); context.install(module)?; diff --git a/crates/rune/src/tests/tuple.rs b/crates/rune/src/tests/tuple.rs index 24d64180b..3c5b83430 100644 --- a/crates/rune/src/tests/tuple.rs +++ b/crates/rune/src/tests/tuple.rs @@ -2,11 +2,15 @@ prelude!(); fn make_module() -> Result { let mut module = Module::new(); - module.function(["receive_tuple"], |(_, _): (Value, Value)| ())?; - module.function( - ["receive_vec_tuple"], - |VecTuple((_, _)): VecTuple<(Value, Value)>| (), - )?; + module + .function("receive_tuple", |(_, _): (Value, Value)| ()) + .build()?; + module + .function( + "receive_vec_tuple", + |VecTuple((_, _)): VecTuple<(Value, Value)>| (), + ) + .build()?; Ok(module) } diff --git a/crates/rune/src/tests/type_name_native.rs b/crates/rune/src/tests/type_name_native.rs index 2a310cf53..ce5570907 100644 --- a/crates/rune/src/tests/type_name_native.rs +++ b/crates/rune/src/tests/type_name_native.rs @@ -18,7 +18,7 @@ impl NativeStruct { fn make_native_module() -> Result { let mut module = Module::with_crate("native_crate")?; module.ty::()?; - module.function(["native_fn"], native_fn)?; + module.function("native_fn", native_fn).build()?; module.associated_function("instance_fn", NativeStruct::instance_fn)?; module.field_function(Protocol::GET, "x", NativeStruct::get_x)?; diff --git a/crates/rune/src/tests/vm_function_pointers.rs b/crates/rune/src/tests/vm_function_pointers.rs index 6d0d3b4d5..966c9e28a 100644 --- a/crates/rune/src/tests/vm_function_pointers.rs +++ b/crates/rune/src/tests/vm_function_pointers.rs @@ -34,7 +34,7 @@ fn vm_execution_unit_fn() -> Result<()> { #[test] fn vm_execution_with_complex_external() -> Result<()> { let mut m = Module::new(); - m.function(["external"], || 42i64)?; + m.function("external", || 42i64).build()?; let mut c1 = Context::with_default_modules()?; c1.install(m)?; diff --git a/examples/examples/native_function.rs b/examples/examples/native_function.rs index 4e9c0608c..5b044b00d 100644 --- a/examples/examples/native_function.rs +++ b/examples/examples/native_function.rs @@ -43,6 +43,6 @@ fn main() -> rune::support::Result<()> { fn module() -> Result { let mut m = Module::new(); - m.function(["add"], |a: i64| a + 1)?; + m.function("add", |a: i64| a + 1).build()?; Ok(m) } diff --git a/examples/examples/vec_args.rs b/examples/examples/vec_args.rs index c7ddf76b1..36fef395a 100644 --- a/examples/examples/vec_args.rs +++ b/examples/examples/vec_args.rs @@ -50,9 +50,10 @@ fn module() -> Result { let mut m = Module::with_item(["mymodule"])?; m.function( - ["pass_along"], + "pass_along", |func: Function, args: Vec| -> VmResult { func.call(args) }, - )?; + ) + .build()?; Ok(m) }