diff --git a/src/typed/generator/luau_type_file_tests.rs b/src/typed/generator/luau_type_file_tests.rs index c6e48d9..29520e0 100644 --- a/src/typed/generator/luau_type_file_tests.rs +++ b/src/typed/generator/luau_type_file_tests.rs @@ -2,7 +2,7 @@ #![cfg(feature = "luau")] use crate::typed::{ - Field, Func, Index, Param, Type, TypedClassBuilder, + Field, Func, Index, Param, Type, Typed, TypedClassBuilder, TypedUserData, function::Return, generator::{Definition, DefinitionBuilder, Definitions, Entry, LuauDefinitionFileGenerator}, }; @@ -906,3 +906,75 @@ fn test_mismatch_enum_tuple_variants_flatten() { "export type Payload = \"None\" | { number | string } | { boolean }" ); } + +/// Verify user data maps correctly +/// This tests a custom struct TestUserData, including attrs, functions and methods +/// It also test UserDataRef and UserDataRefMut in the function / method +#[test] +fn test_user_data() { + struct TestUserData { + attr: String, + } + + impl mlua::UserData for TestUserData {} + + impl Typed for TestUserData { + fn ty() -> Type { + Type::class(TypedClassBuilder::new::().build()) + } + + fn as_param() -> Type { + Type::named("TestUserData") + } + + fn as_return() -> Type { + Self::as_param() + } + } + + impl TypedUserData for TestUserData { + fn add_documentation>(docs: &mut F) { + docs.add("class doc test"); + } + + fn add_fields>(fields: &mut F) { + fields.document("attr doc test"); + fields.add_field_method_get("attr", |_, this| Ok(this.attr.clone())); + } + + fn add_methods>(methods: &mut T) { + methods.document("function doc test"); + methods.add_function("from", |_, other: mlua::UserDataRef| { + Ok(Self { + attr: other.attr.clone(), + }) + }); + methods.document("method doc test"); + methods.add_method("to", |_, this, mut other: mlua::UserDataRefMut| { + (*other).attr = this.attr.clone(); + Ok(()) + }); + } + } + + let out = generate(single( + Definition::start().register_as("TestUserData", TestUserData::ty()), + )); + assert_eq!( + out.trim(), + r#"-- class doc test +declare class TestUserData + -- attr doc test + attr: string + -- method doc test + function to(self, param1: TestUserData): () +end + +-- function doc test +declare function TestUserData_from(param1: TestUserData): TestUserData + +declare TestUserData: { + from: typeof(TestUserData_from), +}"# + ); +} diff --git a/src/typed/generator/type_file_tests.rs b/src/typed/generator/type_file_tests.rs index eb38604..433657e 100644 --- a/src/typed/generator/type_file_tests.rs +++ b/src/typed/generator/type_file_tests.rs @@ -1,7 +1,7 @@ #![cfg(test)] use crate::typed::{ - Index, Param, Type, TypedClassBuilder, + Index, Param, Type, Typed, TypedClassBuilder, TypedUserData, function::Return, generator::{Definition, DefinitionBuilder, DefinitionFileGenerator, Definitions, Entry}, }; @@ -900,6 +900,79 @@ defaultColor = nil"# ); } +/// Verify user data maps correctly +/// This tests a custom struct TestUserData, including attrs, functions and methods +/// It also test UserDataRef and UserDataRefMut in the function / method +#[test] +fn test_user_data() { + struct TestUserData { + attr: String, + } + impl mlua::UserData for TestUserData {} + + impl Typed for TestUserData { + fn ty() -> Type { + Type::class(TypedClassBuilder::new::().build()) + } + + fn as_param() -> Type { + Type::named("TestUserData") + } + + fn as_return() -> Type { + Self::as_param() + } + } + + impl TypedUserData for TestUserData { + fn add_documentation>(docs: &mut F) { + docs.add("class doc test"); + } + + fn add_fields>(fields: &mut F) { + fields.document("attr doc test"); + fields.add_field_method_get("attr", |_, this| Ok(this.attr.clone())); + } + + fn add_methods>(methods: &mut T) { + methods.document("function doc test"); + methods.add_function("from", |_, other: mlua::UserDataRef| { + Ok(Self { + attr: other.attr.clone(), + }) + }); + methods.document("method doc test"); + methods.add_method("to", |_, this, mut other: mlua::UserDataRefMut| { + (*other).attr = this.attr.clone(); + Ok(()) + }); + } + } + + let out = generate(single( + Definition::start().register_as("TestUserData", TestUserData::ty()), + )); + assert_eq!( + out.trim(), + r#"--- @meta + +--- class doc test +--- @class TestUserData +--- attr doc test +--- @field attr string +local _CLASS_TestUserData_ = { + --- function doc test + --- @param param1 TestUserData + --- @return TestUserData + from = function(param1) end, + --- method doc test + --- @param self TestUserData + --- @param param1 TestUserData + to = function(self, param1) end, +}"# + ); +} + // ======================== // lua-language-server validation tests // ======================== diff --git a/src/typed/mod.rs b/src/typed/mod.rs index 63325c7..f23cbc4 100644 --- a/src/typed/mod.rs +++ b/src/typed/mod.rs @@ -16,7 +16,7 @@ use std::{sync::{Arc, Mutex}, cell::{Cell, RefCell}, rc::Rc}; pub use function::{Param, Return, TypedFunction}; -use mlua::{IntoLua, MetaMethod, Value, Variadic}; +use mlua::{IntoLua, MetaMethod, UserDataRef, UserDataRefMut, Value, Variadic}; /// Represents a lua table key /// @@ -508,6 +508,34 @@ impl Typed for mlua::Value { } } +impl Typed for UserDataRef { + fn ty() -> Type { + T::ty() + } + + fn as_param() -> Type { + T::as_param() + } + + fn as_return() -> Type { + T::as_return() + } +} + +impl Typed for UserDataRefMut { + fn ty() -> Type { + T::ty() + } + + fn as_param() -> Type { + T::as_param() + } + + fn as_return() -> Type { + T::as_return() + } +} + impl Typed for Variadic { /// ...type fn ty() -> Type {