teal: add documentation for types, fields, and functions
ci/woodpecker/push/debug Pipeline was successful Details

This commit is contained in:
SeanOMik 2024-02-28 20:00:13 -05:00
parent 4c1a1588c5
commit feb93f2b4e
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
3 changed files with 74 additions and 9 deletions

View File

@ -17,6 +17,7 @@ pub struct FunctionDesc {
pub args: Vec<FuncArg>, pub args: Vec<FuncArg>,
/// The return type of the function, `None` if it returns nothing /// The return type of the function, `None` if it returns nothing
pub return_ty: Option<Type>, pub return_ty: Option<Type>,
pub docs: Option<Vec<String>>,
} }
impl GenerateTealDefinition for FunctionDesc { impl GenerateTealDefinition for FunctionDesc {
@ -58,6 +59,7 @@ pub struct FieldDesc {
pub ty: Type, pub ty: Type,
/// A boolean indicating if there is a setter for this field. /// A boolean indicating if there is a setter for this field.
pub can_set: bool, pub can_set: bool,
pub docs: Vec<String>,
} }
impl GenerateTealDefinition for FieldDesc { impl GenerateTealDefinition for FieldDesc {

View File

@ -42,7 +42,10 @@ where
inner: MutCow<'a, UserdataBuilder<'a, T>>, inner: MutCow<'a, UserdataBuilder<'a, T>>,
pub(crate) fields: HashMap<String, FieldDesc>, pub(crate) fields: HashMap<String, FieldDesc>,
pub(crate) funcs: HashMap<String, FunctionDesc>, pub(crate) funcs: HashMap<String, FunctionDesc>,
pub(crate) type_docs: Vec<String>, // each element is a new line
func_arg_names: Option<Vec<String>>, func_arg_names: Option<Vec<String>>,
/// docs that will be added to a field or function that will soon be added
pub(crate) queued_docs: Option<Vec<String>>,
} }
impl<'a, T: Userdata> From<&'a mut UserdataBuilder<'a, T>> for TealUserdataBuilder<'a, T> { 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), inner: MutCow::Borrowed(value),
funcs: Default::default(), funcs: Default::default(),
fields: Default::default(), fields: Default::default(),
type_docs: Default::default(),
func_arg_names: None, func_arg_names: None,
queued_docs: None,
} }
} }
} }
pub(crate) fn function_desc<'a, A, R>(func_arg_names: &mut Option<Vec<String>>, fn_name: &str) -> FunctionDesc pub(crate) fn function_desc<'a, A, R>(func_arg_names: &mut Option<Vec<String>>, docs: &mut Option<Vec<String>>, fn_name: &str) -> FunctionDesc
where where
A: FromLuaVec<'a> + LuaTypeName, A: FromLuaVec<'a> + LuaTypeName,
R: AsLua<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName,
@ -118,6 +123,8 @@ where
} }
}; };
let docs = docs.take();
let ret_name = R::get_lua_name(); let ret_name = R::get_lua_name();
let ret_ty = match ret_name.as_str() { let ret_ty = match ret_name.as_str() {
"()" => None, "()" => None,
@ -129,6 +136,7 @@ where
is_field: true, is_field: true,
args, args,
return_ty: ret_ty, return_ty: ret_ty,
docs,
}; };
func_desc func_desc
@ -140,16 +148,38 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
inner: MutCow::Owned(UserdataBuilder::new()), inner: MutCow::Owned(UserdataBuilder::new()),
fields: Default::default(), fields: Default::default(),
funcs: Default::default(), funcs: Default::default(),
type_docs: Default::default(),
func_arg_names: None, 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<F, R>(&mut self, name: &str, f: F) -> &mut Self pub fn field_getter<F, R>(&mut self, name: &str, f: F) -> &mut Self
where where
F: Fn(&'a State, &T) -> crate::Result<R> + 'static, F: Fn(&'a State, &T) -> crate::Result<R> + 'static,
R: AsLua<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static T: TealUserdata + 'static
{ {
let docs = self.queued_docs.take().unwrap_or_default();
let tname = R::get_lua_name(); let tname = R::get_lua_name();
// maybe field_setter was ran first // maybe field_setter was ran first
if !self.fields.contains_key(&tname) { if !self.fields.contains_key(&tname) {
@ -159,6 +189,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
name: tname.clone(), name: tname.clone(),
}, },
can_set: false, can_set: false,
docs,
}; };
self.fields.insert(tname, desc); self.fields.insert(tname, desc);
@ -174,6 +205,8 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
V: FromLua<'a> + LuaTypeName, V: FromLua<'a> + LuaTypeName,
T: TealUserdata + 'static T: TealUserdata + 'static
{ {
let docs = self.queued_docs.take().unwrap_or_default();
let tname = V::get_lua_name(); let tname = V::get_lua_name();
if let Some(field) = self.fields.get_mut(&tname) { if let Some(field) = self.fields.get_mut(&tname) {
field.can_set = true; field.can_set = true;
@ -184,6 +217,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
name: tname.clone(), name: tname.clone(),
}, },
can_set: true, can_set: true,
docs,
}; };
self.fields.insert(tname, desc); self.fields.insert(tname, desc);
@ -199,7 +233,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
A: FromLuaVec<'a> + LuaTypeName, A: FromLuaVec<'a> + LuaTypeName,
R: AsLua<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName,
{ {
let func_desc = function_desc::<A, R>(&mut self.func_arg_names, name); let func_desc = function_desc::<A, R>(&mut self.func_arg_names, &mut self.queued_docs, name);
self.funcs.insert(name.to_string(), func_desc); self.funcs.insert(name.to_string(), func_desc);
self.inner.function(name, f); self.inner.function(name, f);
@ -213,7 +247,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
R: AsLua<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static T: TealUserdata + 'static
{ {
let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, name); let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, &mut self.queued_docs, name);
let mut tmp = vec![FuncArg { let mut tmp = vec![FuncArg {
name: Some("self".to_string()), name: Some("self".to_string()),
ty: Type { ty: Type {
@ -236,7 +270,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
R: AsLua<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static T: TealUserdata + 'static
{ {
let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, name); let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, &mut self.queued_docs, name);
let mut tmp = vec![FuncArg { let mut tmp = vec![FuncArg {
name: Some("self".to_string()), name: Some("self".to_string()),
ty: Type { ty: Type {
@ -260,7 +294,7 @@ impl<'a, T: Userdata> TealUserdataBuilder<'a, T> {
R: AsLua<'a> + LuaTypeName, R: AsLua<'a> + LuaTypeName,
T: TealUserdata + 'static T: TealUserdata + 'static
{ {
let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, name.as_ref()); let mut func_desc = function_desc::<A, R>(&mut self.func_arg_names, &mut self.queued_docs, name.as_ref());
let mut tmp = vec![FuncArg { let mut tmp = vec![FuncArg {
name: Some("self".to_string()), name: Some("self".to_string()),
ty: Type { ty: Type {

View File

@ -5,6 +5,9 @@ pub struct TypeWalker {
records: Vec<Record>, records: Vec<Record>,
global_fields: Vec<FieldDesc>, global_fields: Vec<FieldDesc>,
global_funcs: Vec<FunctionDesc>, global_funcs: Vec<FunctionDesc>,
func_arg_names: Option<Vec<String>>,
/// docs that will be added to a field or function that will soon be added
queued_docs: Option<Vec<String>>,
} }
impl TypeWalker { impl TypeWalker {
@ -28,23 +31,47 @@ impl TypeWalker {
self.records.push(record); 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<T: LuaTypeName>(&mut self, name: &str) { pub fn add_global_field<T: LuaTypeName>(&mut self, name: &str) {
let docs = self.queued_docs.take().unwrap_or_default();
let field = FieldDesc { let field = FieldDesc {
name: name.to_string(), name: name.to_string(),
ty: Type::new::<T>(), ty: Type::new::<T>(),
can_set: false, can_set: false,
docs,
}; };
self.global_fields.push(field); 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 where
Args: FromLuaVec<'a> + LuaTypeName, Args: FromLuaVec<'a> + LuaTypeName,
Ret: AsLua<'a> + LuaTypeName Ret: AsLua<'a> + LuaTypeName
{ {
let arg_names: Vec<_> = arg_names.iter().map(|s| s.to_string()).collect(); let mut desc = function_desc::<Args, Ret>(&mut self.func_arg_names, &mut self.queued_docs, name);
let mut desc = function_desc::<Args, Ret>(&mut Some(arg_names), name);
desc.is_field = true; desc.is_field = true;
self.global_funcs.push(desc); self.global_funcs.push(desc);
@ -86,7 +113,9 @@ mod tests {
let mut walker = TypeWalker::new(); let mut walker = TypeWalker::new();
walker.walk_type::<Vec3>(); walker.walk_type::<Vec3>();
walker.add_global_field::<f64>("multiplier"); walker.add_global_field::<f64>("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()); println!("{}", walker.to_type_def());
} }