From feb93f2b4ed7063e4fba366377b039ba4f9026ef Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Wed, 28 Feb 2024 20:00:13 -0500 Subject: [PATCH] teal: add documentation for types, fields, and functions --- src/teal/fn_desc.rs | 2 ++ src/teal/userdata.rs | 44 +++++++++++++++++++++++++++++++++++++++----- src/teal/walker.rs | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/src/teal/fn_desc.rs b/src/teal/fn_desc.rs index 7c7ca6b..d8bde0b 100644 --- a/src/teal/fn_desc.rs +++ b/src/teal/fn_desc.rs @@ -17,6 +17,7 @@ pub struct FunctionDesc { pub args: Vec, /// The return type of the function, `None` if it returns nothing pub return_ty: Option, + pub docs: Option>, } impl GenerateTealDefinition for FunctionDesc { @@ -58,6 +59,7 @@ pub struct FieldDesc { pub ty: Type, /// A boolean indicating if there is a setter for this field. pub can_set: bool, + pub docs: Vec, } impl GenerateTealDefinition for FieldDesc { diff --git a/src/teal/userdata.rs b/src/teal/userdata.rs index 12781d4..375f8fd 100644 --- a/src/teal/userdata.rs +++ b/src/teal/userdata.rs @@ -42,7 +42,10 @@ where inner: MutCow<'a, UserdataBuilder<'a, T>>, pub(crate) fields: HashMap, pub(crate) funcs: HashMap, + pub(crate) type_docs: Vec, // each element is a new line func_arg_names: Option>, + /// docs that will be added to a field or function that will soon be added + pub(crate) queued_docs: Option>, } impl<'a, T: Userdata> From<&'a mut UserdataBuilder<'a, T>> for TealUserdataBuilder<'a, T> { @@ -51,12 +54,14 @@ impl<'a, T: Userdata> From<&'a mut UserdataBuilder<'a, T>> for TealUserdataBuild inner: MutCow::Borrowed(value), funcs: Default::default(), fields: Default::default(), + type_docs: Default::default(), func_arg_names: None, + queued_docs: None, } } } -pub(crate) fn function_desc<'a, A, R>(func_arg_names: &mut Option>, fn_name: &str) -> FunctionDesc +pub(crate) fn function_desc<'a, A, R>(func_arg_names: &mut Option>, docs: &mut Option>, fn_name: &str) -> FunctionDesc where A: FromLuaVec<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName, @@ -118,6 +123,8 @@ where } }; + let docs = docs.take(); + let ret_name = R::get_lua_name(); let ret_ty = match ret_name.as_str() { "()" => None, @@ -129,6 +136,7 @@ where is_field: true, args, return_ty: ret_ty, + docs, }; func_desc @@ -140,16 +148,38 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { inner: MutCow::Owned(UserdataBuilder::new()), fields: Default::default(), funcs: Default::default(), + type_docs: Default::default(), func_arg_names: None, + queued_docs: None, } } + /// Add documentation for this type + pub fn document_type(&mut self, text: &str) -> &mut Self { + self.type_docs.push(text.to_string()); + self + } + + /// Add documentation for a field, or function that will added right after this + pub fn document(&mut self, text: &str) -> &mut Self { + if self.queued_docs.is_none() { + self.queued_docs = Some(Vec::default()); + } + + let docs = self.queued_docs.as_mut().unwrap(); + docs.push(text.to_string()); + + self + } + pub fn field_getter(&mut self, name: &str, f: F) -> &mut Self where F: Fn(&'a State, &T) -> crate::Result + 'static, R: AsLua<'a> + LuaTypeName, T: TealUserdata + 'static { + let docs = self.queued_docs.take().unwrap_or_default(); + let tname = R::get_lua_name(); // maybe field_setter was ran first if !self.fields.contains_key(&tname) { @@ -159,6 +189,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { name: tname.clone(), }, can_set: false, + docs, }; self.fields.insert(tname, desc); @@ -174,6 +205,8 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { V: FromLua<'a> + LuaTypeName, T: TealUserdata + 'static { + let docs = self.queued_docs.take().unwrap_or_default(); + let tname = V::get_lua_name(); if let Some(field) = self.fields.get_mut(&tname) { field.can_set = true; @@ -184,6 +217,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { name: tname.clone(), }, can_set: true, + docs, }; self.fields.insert(tname, desc); @@ -199,7 +233,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { A: FromLuaVec<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName, { - let func_desc = function_desc::(&mut self.func_arg_names, name); + let func_desc = function_desc::(&mut self.func_arg_names, &mut self.queued_docs, name); self.funcs.insert(name.to_string(), func_desc); self.inner.function(name, f); @@ -213,7 +247,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { R: AsLua<'a> + LuaTypeName, T: TealUserdata + 'static { - let mut func_desc = function_desc::(&mut self.func_arg_names, name); + let mut func_desc = function_desc::(&mut self.func_arg_names, &mut self.queued_docs, name); let mut tmp = vec![FuncArg { name: Some("self".to_string()), ty: Type { @@ -236,7 +270,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { R: AsLua<'a> + LuaTypeName, T: TealUserdata + 'static { - let mut func_desc = function_desc::(&mut self.func_arg_names, name); + let mut func_desc = function_desc::(&mut self.func_arg_names, &mut self.queued_docs, name); let mut tmp = vec![FuncArg { name: Some("self".to_string()), ty: Type { @@ -260,7 +294,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> { R: AsLua<'a> + LuaTypeName, T: TealUserdata + 'static { - let mut func_desc = function_desc::(&mut self.func_arg_names, name.as_ref()); + let mut func_desc = function_desc::(&mut self.func_arg_names, &mut self.queued_docs, name.as_ref()); let mut tmp = vec![FuncArg { name: Some("self".to_string()), ty: Type { diff --git a/src/teal/walker.rs b/src/teal/walker.rs index 18e199e..4603b45 100644 --- a/src/teal/walker.rs +++ b/src/teal/walker.rs @@ -5,6 +5,9 @@ pub struct TypeWalker { records: Vec, global_fields: Vec, global_funcs: Vec, + func_arg_names: Option>, + /// docs that will be added to a field or function that will soon be added + queued_docs: Option>, } impl TypeWalker { @@ -28,23 +31,47 @@ impl TypeWalker { self.records.push(record); } + /// Add documentation for a field, or function that will added right after this + pub fn document(&mut self, text: &str) -> &mut Self { + if self.queued_docs.is_none() { + self.queued_docs = Some(Vec::default()); + } + + let docs = self.queued_docs.as_mut().unwrap(); + docs.push(text.to_string()); + + self + } + pub fn add_global_field(&mut self, name: &str) { + let docs = self.queued_docs.take().unwrap_or_default(); + let field = FieldDesc { name: name.to_string(), ty: Type::new::(), can_set: false, + docs, }; self.global_fields.push(field); } - pub fn add_global_func<'a, Args, Ret>(&mut self, name: &str, arg_names: &[&str]) + pub fn set_arg_names(&mut self, names: &[&str]) -> &mut Self { + debug_assert!(self.func_arg_names.is_none(), + "Invalid attempt to set argument names for function. \ + There are arguments that are currently pending to be set on a function"); + + self.func_arg_names = Some(names.iter().map(|n| n.to_string()).collect()); + + self + } + + pub fn add_global_func<'a, Args, Ret>(&mut self, name: &str) where Args: FromLuaVec<'a> + LuaTypeName, Ret: AsLua<'a> + LuaTypeName { - let arg_names: Vec<_> = arg_names.iter().map(|s| s.to_string()).collect(); - let mut desc = function_desc::(&mut Some(arg_names), name); + let mut desc = function_desc::(&mut self.func_arg_names, &mut self.queued_docs, name); desc.is_field = true; self.global_funcs.push(desc); @@ -86,7 +113,9 @@ mod tests { let mut walker = TypeWalker::new(); walker.walk_type::(); walker.add_global_field::("multiplier"); - walker.add_global_func::<(f32, f64, usize), i32>("do_cool_math", &["a", "b", "z"]); + walker.document("This function does some very cool math that does a lot."); + walker.set_arg_names(&["a", "b", "z"]); + walker.add_global_func::<(f32, f64, usize), i32>("do_cool_math"); println!("{}", walker.to_type_def()); }