diff --git a/src/module/mod.rs b/src/module/mod.rs index 4e7bc7a8..7a4a25ae 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -47,9 +47,12 @@ pub struct FuncInfo { pub params: usize, /// Parameter types (if applicable). pub param_types: StaticVec, - /// Parameter names (if available). + /// Parameter names and types (if available). #[cfg(feature = "metadata")] - pub param_names: StaticVec, + pub param_names_and_types: StaticVec, + /// Return type name. + #[cfg(feature = "metadata")] + pub return_type_name: Identifier, } impl FuncInfo { @@ -60,16 +63,21 @@ impl FuncInfo { pub fn gen_signature(&self) -> String { let mut sig = format!("{}(", self.name); - if !self.param_names.is_empty() { - let mut params: StaticVec> = - self.param_names.iter().map(|s| s.as_str().into()).collect(); - let return_type = params.pop().unwrap_or_else(|| "()".into()); + if !self.param_names_and_types.is_empty() { + let params: StaticVec<_> = self + .param_names_and_types + .iter() + .map(|s| s.as_str()) + .collect(); sig.push_str(¶ms.join(", ")); - if &*return_type != "()" { - sig.push_str(") -> "); - sig.push_str(&return_type); - } else { - sig.push(')'); + sig.push_str(")"); + + match self.return_type_name.as_str() { + "" | "()" => (), + ty => { + sig.push_str(" -> "); + sig.push_str(ty); + } } } else { for x in 0..self.params { @@ -82,7 +90,12 @@ impl FuncInfo { if self.func.is_script() { sig.push(')'); } else { - sig.push_str(") -> ?"); + sig.push_str(")"); + + match self.return_type_name.as_str() { + "()" => (), + _ => sig.push_str(" -> ?"), + } } } @@ -457,8 +470,12 @@ impl Module { // None + function name + number of arguments. let num_params = fn_def.params.len(); let hash_script = crate::calc_fn_hash(&fn_def.name, num_params); - let mut param_names = fn_def.params.clone(); - param_names.push("Dynamic".into()); + #[cfg(feature = "metadata")] + let param_names_and_types = fn_def + .params + .iter() + .map(|v| self.identifiers.get(v.as_str())) + .collect(); self.functions.insert( hash_script, FuncInfo { @@ -468,7 +485,9 @@ impl Module { params: num_params, param_types: StaticVec::new_const(), #[cfg(feature = "metadata")] - param_names, + param_names_and_types, + #[cfg(feature = "metadata")] + return_type_name: self.identifiers.get("Dynamic"), func: Into::::into(fn_def).into(), } .into(), @@ -613,13 +632,20 @@ impl Module { #[cfg(feature = "metadata")] #[inline] pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[impl AsRef]) -> &mut Self { - let param_names = arg_names + let mut param_names: StaticVec<_> = arg_names .iter() .map(|name| self.identifiers.get(name.as_ref())) .collect(); if let Some(f) = self.functions.get_mut(&hash_fn) { - f.param_names = param_names; + let (param_names, return_type_name) = if param_names.len() > f.params { + let return_type = param_names.pop().expect("exists"); + (param_names, return_type) + } else { + (param_names, Default::default()) + }; + f.param_names_and_types = param_names; + f.return_type_name = return_type_name; } self @@ -664,6 +690,15 @@ impl Module { /// # WARNING - Low Level API /// /// This function is very low level. + /// + /// ## Parameter Names and Types + /// + /// Each parameter name/type pair should be a single string of the format: `var_name: type`. + /// + /// ## Return Type + /// + /// The _last entry_ in the list should be the _return type_ of the function. + /// In other words, the number of entries should be one larger than the number of parameters. #[inline] pub fn set_fn( &mut self, @@ -686,13 +721,20 @@ impl Module { param_types.shrink_to_fit(); #[cfg(feature = "metadata")] - let mut param_names: StaticVec<_> = _arg_names - .iter() - .flat_map(|&p| p.iter()) - .map(|&arg| self.identifiers.get(arg)) - .collect(); - #[cfg(feature = "metadata")] - param_names.shrink_to_fit(); + let (param_names, return_type_name) = { + let mut names = _arg_names + .iter() + .flat_map(|&p| p.iter()) + .map(|&arg| self.identifiers.get(arg)) + .collect::>(); + let return_type = if names.len() > arg_types.len() { + names.pop().expect("exists") + } else { + Default::default() + }; + names.shrink_to_fit(); + (names, return_type) + }; let hash_fn = calc_native_fn_hash(empty::<&str>(), name.as_ref(), ¶m_types); @@ -705,7 +747,9 @@ impl Module { params: param_types.len(), param_types, #[cfg(feature = "metadata")] - param_names, + param_names_and_types: param_names, + #[cfg(feature = "metadata")] + return_type_name, func: func.into(), } .into(), @@ -1284,8 +1328,8 @@ impl Module { /// Get an iterator to the sub-modules in the [`Module`]. #[inline] - pub fn iter_sub_modules(&self) -> impl Iterator)> { - self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) + pub fn iter_sub_modules(&self) -> impl Iterator)> { + self.modules.iter().map(|(k, m)| (k.as_str(), m)) } /// Get an iterator to the variables in the [`Module`]. diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index df6364fe..2aa999b7 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -48,33 +48,24 @@ impl From for FnAccess { #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -struct FnParam { - pub name: Box, +struct FnParam<'a> { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub typ: Option>, + pub name: Option<&'a str>, + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub typ: Option<&'a str>, } -impl PartialOrd for FnParam { +impl PartialOrd for FnParam<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(match self.name.partial_cmp(&other.name).expect("succeed") { Ordering::Less => Ordering::Less, Ordering::Greater => Ordering::Greater, - Ordering::Equal => match (self.typ.is_none(), other.typ.is_none()) { - (true, true) => Ordering::Equal, - (true, false) => Ordering::Greater, - (false, true) => Ordering::Less, - (false, false) => self - .typ - .as_ref() - .expect("`Some`") - .partial_cmp(other.typ.as_ref().expect("`Some`")) - .expect("succeed"), - }, + Ordering::Equal => self.typ.partial_cmp(other.typ).expect("succeed"), }) } } -impl Ord for FnParam { +impl Ord for FnParam<'_> { fn cmp(&self, other: &Self) -> Ordering { match self.name.cmp(&other.name) { Ordering::Equal => self.typ.cmp(&other.typ), @@ -85,7 +76,7 @@ impl Ord for FnParam { #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -struct FnMetadata { +struct FnMetadata<'a> { pub base_hash: u64, pub full_hash: u64, pub namespace: FnNamespace, @@ -95,21 +86,21 @@ struct FnMetadata { pub typ: FnType, pub num_params: usize, #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub params: Vec, + pub params: Vec>, #[serde(default, skip_serializing_if = "Option::is_none")] - pub return_type: Option>, + pub return_type_name: Option<&'a str>, pub signature: String, #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub doc_comments: Vec>, + pub doc_comments: Vec<&'a str>, } -impl PartialOrd for FnMetadata { +impl PartialOrd for FnMetadata<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for FnMetadata { +impl Ord for FnMetadata<'_> { fn cmp(&self, other: &Self) -> Ordering { match self.name.cmp(&other.name) { Ordering::Equal => match self.num_params.cmp(&other.num_params) { @@ -121,8 +112,8 @@ impl Ord for FnMetadata { } } -impl From<&crate::module::FuncInfo> for FnMetadata { - fn from(info: &crate::module::FuncInfo) -> Self { +impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> { + fn from(info: &'a crate::module::FuncInfo) -> Self { let base_hash = calc_fn_hash(&info.name, info.params); let (typ, full_hash) = if info.func.is_script() { (FnType::Script, base_hash) @@ -142,24 +133,22 @@ impl From<&crate::module::FuncInfo> for FnMetadata { typ, num_params: info.params, params: info - .param_names + .param_names_and_types .iter() - .take(info.params) .map(|s| { let mut seg = s.splitn(2, ':'); - let name = seg - .next() - .map(|s| s.trim().into()) - .unwrap_or_else(|| "_".into()); - let typ = seg.next().map(|s| s.trim().into()); + let name = match seg.next().map(&str::trim).unwrap_or("_") { + "_" => None, + s => Some(s), + }; + let typ = seg.next().map(&str::trim); FnParam { name, typ } }) .collect(), - return_type: info - .param_names - .last() - .map(|s| s.as_str().into()) - .or_else(|| Some("()".into())), + return_type_name: match info.return_type_name.as_str() { + "" | "()" => None, + ty => Some(ty), + }, signature: info.gen_signature(), doc_comments: if info.func.is_script() { #[cfg(feature = "no_function")] @@ -171,7 +160,7 @@ impl From<&crate::module::FuncInfo> for FnMetadata { .expect("script-defined function") .comments .as_ref() - .map_or_else(|| Vec::new(), |v| v.to_vec()) + .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) } else { Vec::new() }, @@ -181,14 +170,14 @@ impl From<&crate::module::FuncInfo> for FnMetadata { #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] -struct ModuleMetadata { +struct ModuleMetadata<'a> { #[serde(skip_serializing_if = "BTreeMap::is_empty")] - pub modules: BTreeMap, + pub modules: BTreeMap<&'a str, Self>, #[serde(skip_serializing_if = "Vec::is_empty")] - pub functions: Vec, + pub functions: Vec>, } -impl ModuleMetadata { +impl ModuleMetadata<'_> { #[inline(always)] pub fn new() -> Self { Self { @@ -198,15 +187,15 @@ impl ModuleMetadata { } } -impl From<&crate::Module> for ModuleMetadata { - fn from(module: &crate::Module) -> Self { +impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> { + fn from(module: &'a crate::Module) -> Self { let mut functions: Vec<_> = module.iter_fn().map(|f| f.into()).collect(); functions.sort(); Self { modules: module .iter_sub_modules() - .map(|(name, m)| (name.to_string(), m.as_ref().into())) + .map(|(name, m)| (name, m.as_ref().into())) .collect(), functions, } @@ -232,20 +221,14 @@ impl Engine { let _ast = ast; let mut global = ModuleMetadata::new(); - if include_global { - self.global_modules - .iter() - .take(self.global_modules.len() - 1) - .flat_map(|m| m.iter_fn()) - .for_each(|f| global.functions.push(f.into())); - } - self.global_sub_modules.iter().for_each(|(name, m)| { - global.modules.insert(name.to_string(), m.as_ref().into()); + global.modules.insert(name, m.as_ref().into()); }); - self.global_namespace() - .iter_fn() + self.global_modules + .iter() + .take(if include_global { usize::MAX } else { 1 }) + .flat_map(|m| m.iter_fn()) .for_each(|f| global.functions.push(f.into())); #[cfg(not(feature = "no_function"))]