diff --git a/Cargo.toml b/Cargo.toml index ba19d03..55a974e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "native_api_1c_macro", "sample_addin_rs", ] +resolver = "2" [patch.crates-io] native_api_1c = { path = "native_api_1c" } diff --git a/README.md b/README.md index 86ae62a..cef92b5 100644 --- a/README.md +++ b/README.md @@ -59,17 +59,18 @@ Available property types: `i32`, `f64`, `bool`, `String` - `name_ru` - property name in 1C in Russian ### Input arguments, `#[arg(ty = ...)]`, for each type of argument must be set, on of: | Type definition | Rust type | 1C type | -|-----------------|-------------------------|-------------------------| +| --------------- | ----------------------- | ----------------------- | | `Int` | `i32` | `Number` (Int) | | `Float` | `f64` | `Number` (Float or Int) | | `Bool` | `bool` | `Boolean` | | `Str` | `String` | `String` | | `Date` | `chrono::NaiveDateTime` | `Date` | | `Blob` | `Vec` | `BinaryData` | +| `Any` | `ParamValue` | `Any` | ### Return values, `#[returns(ty = ...)]`, type must be set, one of: | Type definition | Rust type | 1C type | -|-----------------|-------------------------|--------------| +| --------------- | ----------------------- | ------------ | | `Int` | `i32` | `Number` | | `Float` | `f64` | `Number` | | `Bool` | `bool` | `Boolean` | @@ -77,6 +78,7 @@ Available property types: `i32`, `f64`, `bool`, `String` | `Date` | `chrono::NaiveDateTime` | `Date` | | `Blob` | `Vec` | `BinaryData` | | `None` | `()` | `Undefined` | +| `Any` | `ParamValue` | `Any` | Additionally, `Result` can be used, where `T` is one of the above. In this case, `result` must be set in `#[returns(...)]` attribute: `#[returns(Int, result)]` for `Result` @@ -103,7 +105,7 @@ native_api_1c = "0.10.5" use std::sync::Arc; use native_api_1c::{ - native_api_1c_core::ffi::connection::Connection, + native_api_1c_core::{ffi::connection::Connection, interface::ParamValue}, native_api_1c_macro::{extern_functions, AddIn}, }; @@ -139,6 +141,13 @@ pub struct SampleAddIn { #[add_in_func(name = "MyProcedure", name_ru = "МояПроцедура")] pub my_procedure: fn(&mut Self), + /// Function that accepts Any types + #[add_in_func(name = "CompareAny", name_ru = "СравнитьЛюбое")] + #[arg(ty = Any)] + #[arg(ty = Any)] + #[returns(ty = Bool)] + pub compare_any: fn(ParamValue, ParamValue) -> bool, + /// Private field, not visible from 1C private_field: i32, } @@ -152,6 +161,7 @@ impl Default for SampleAddIn { my_function: Self::my_function_inner, my_procedure: Self::my_procedure_inner, private_field: 100, + compare_any: Self::compare_any, } } } @@ -168,6 +178,10 @@ impl SampleAddIn { fn my_procedure_inner(&mut self) { self.protected_prop += 10; } + + fn compare_any(arg1: ParamValue, arg2: ParamValue) -> bool { + arg1 == arg2 + } } extern_functions! { diff --git a/native_api_1c_core/src/ffi/mod.rs b/native_api_1c_core/src/ffi/mod.rs index fdbd949..ef07921 100644 --- a/native_api_1c_core/src/ffi/mod.rs +++ b/native_api_1c_core/src/ffi/mod.rs @@ -61,8 +61,8 @@ mod offset { pub const USER_LANG: usize = 3; } -impl<'a, const OFFSET: usize, T: AddInWrapper> This { - unsafe fn get_component(&mut self) -> &'a mut Component { +impl This { + unsafe fn get_component(&mut self) -> &mut Component { let new_ptr = (self as *mut This as *mut c_void) .sub(OFFSET * std::mem::size_of::()); &mut *(new_ptr as *mut Component) diff --git a/native_api_1c_core/src/ffi/string_utils.rs b/native_api_1c_core/src/ffi/string_utils.rs index 982c744..2b80f1f 100644 --- a/native_api_1c_core/src/ffi/string_utils.rs +++ b/native_api_1c_core/src/ffi/string_utils.rs @@ -27,7 +27,7 @@ pub unsafe fn get_str<'a>(s: *const u16) -> &'a [u16] { /// `Vec` - UTF-16 string without null terminator #[cfg(target_family = "unix")] pub fn os_string_nil(s: &str) -> Vec { - s.encode_utf16().collect() + s.encode_utf16().chain(Some(0)).collect() } /// Helper function to convert Rust string to UTF-16 string diff --git a/native_api_1c_core/src/interface.rs b/native_api_1c_core/src/interface.rs index 1fe369a..339a380 100644 --- a/native_api_1c_core/src/interface.rs +++ b/native_api_1c_core/src/interface.rs @@ -83,7 +83,7 @@ impl ParamValues { self.values.is_empty() } - pub fn iter(&self) -> std::slice::Iter { + pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, ParamValue> { self.values.iter() } } diff --git a/native_api_1c_macro/src/derive_addin/constants.rs b/native_api_1c_macro/src/derive_addin/constants.rs index 14c1d79..f04b788 100644 --- a/native_api_1c_macro/src/derive_addin/constants.rs +++ b/native_api_1c_macro/src/derive_addin/constants.rs @@ -4,6 +4,7 @@ pub const F64_TYPE: &str = "Float"; pub const STRING_TYPE: &str = "Str"; pub const DATE_TYPE: &str = "Date"; pub const BLOB_TYPE: &str = "Blob"; +pub const ANY_TYPE: &str = "Any"; pub const UNTYPED_TYPE: &str = "None"; pub const ALL_RETURN_TYPES: &[&str] = &[ @@ -13,6 +14,7 @@ pub const ALL_RETURN_TYPES: &[&str] = &[ STRING_TYPE, DATE_TYPE, BLOB_TYPE, + ANY_TYPE, UNTYPED_TYPE, ]; pub const ALL_ARG_TYPES: &[&str] = &[ @@ -22,4 +24,5 @@ pub const ALL_ARG_TYPES: &[&str] = &[ STRING_TYPE, DATE_TYPE, BLOB_TYPE, + ANY_TYPE, ]; diff --git a/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_func.rs b/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_func.rs index 4ddf411..8f21937 100644 --- a/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_func.rs +++ b/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_func.rs @@ -20,24 +20,24 @@ impl Default for CallAsFuncCollector { impl<'a> FromIterator<(usize, &'a FuncDesc)> for CallAsFuncCollector { fn from_iter>(iter: T) -> Self { - let mut body = TokenStream::new(); + let mut tkn_func_calls_with_selectors = vec![]; for (func_index, func_desc) in iter { + // Skip functions without return value if func_desc.return_value.ty.is_none() { - // Skip functions without return value continue; } - let return_val_ident = Ident::new("val", proc_macro2::Span::call_site()); - let call_func = func_call_tkn(func_desc, Some(&return_val_ident)); - body.extend(quote! { + let ident_return_val = Ident::new("val", proc_macro2::Span::call_site()); + let tkn_func_call = func_call_tkn(func_desc, Some(&ident_return_val)); + tkn_func_calls_with_selectors.push(quote! { if method_num == #func_index { - #call_func - return Ok(val); + #tkn_func_call + return Ok(#ident_return_val); }; }); } - let definition = quote! { + let definition: TokenStream = quote! { fn call_as_func( &mut self, method_num: usize, @@ -45,7 +45,9 @@ impl<'a> FromIterator<(usize, &'a FuncDesc)> for CallAsFuncCollector { ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult< native_api_1c::native_api_1c_core::interface::ParamValue > { - #body + #(#tkn_func_calls_with_selectors)* + + // platform call was invalid Err(()) } }; diff --git a/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_proc.rs b/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_proc.rs index 5d0e2f8..9c60698 100644 --- a/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_proc.rs +++ b/native_api_1c_macro/src/derive_addin/functions/collectors/call_as_proc.rs @@ -19,12 +19,12 @@ impl Default for CallAsProcCollector { impl<'a> FromIterator<(usize, &'a FuncDesc)> for CallAsProcCollector { fn from_iter>(iter: T) -> Self { - let mut body = TokenStream::new(); + let mut tkn_func_calls_with_selectors = vec![]; for (func_index, func_desc) in iter { - let call_func = func_call_tkn(func_desc, None); - body.extend(quote! { + let tkn_func_call = func_call_tkn(func_desc, None); + tkn_func_calls_with_selectors.push(quote! { if method_num == #func_index { - #call_func + #tkn_func_call return Ok(()); }; }); @@ -36,7 +36,9 @@ impl<'a> FromIterator<(usize, &'a FuncDesc)> for CallAsProcCollector { method_num: usize, params: &mut native_api_1c::native_api_1c_core::interface::ParamValues, ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult<()> { - #body + #(#tkn_func_calls_with_selectors)* + + // platform call was invalid Err(()) } }; diff --git a/native_api_1c_macro/src/derive_addin/functions/generate.rs b/native_api_1c_macro/src/derive_addin/functions/generate.rs index d8cc204..e955543 100644 --- a/native_api_1c_macro/src/derive_addin/functions/generate.rs +++ b/native_api_1c_macro/src/derive_addin/functions/generate.rs @@ -1,3 +1,5 @@ +use std::vec; + use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::Ident; @@ -9,95 +11,104 @@ use super::{FuncArgumentDesc, FuncDesc, FuncParamType}; pub fn func_call_tkn(func: &FuncDesc, set_to: Option<&Ident>) -> TokenStream { let func_ident = func.ident.clone(); - let mut pre_call = quote! {}; - let mut func_args = quote! {}; - let mut post_call = quote! {}; + let mut tkn_func_args = vec![]; + if func.has_self_param() { + tkn_func_args.push(quote! {self}); + } + + let mut tkn_param_pre_calls = vec![]; + let mut tkn_param_post_calls = vec![]; for (param_index, param_desc) in func.get_1c_params().iter().enumerate() { let param_ident = Ident::new(&format!("param_{}", param_index + 1), Span::call_site()); - let (pre_call_param, post_call_param) = - gen_param_prep(param_desc, param_index, ¶m_ident); - - if func_args.is_empty() { - func_args.extend(quote! {#param_ident}) - } else { - func_args.extend(quote! {, #param_ident}); - } - - pre_call.extend(pre_call_param); - post_call.extend(post_call_param); - } - - if func.has_self_param() { - if func_args.is_empty() { - func_args = quote! {self}; - } else { - func_args = quote! {self, #func_args}; - } + tkn_param_pre_calls.push(param_pre_call_tkn(param_desc, param_index, ¶m_ident)); + tkn_param_post_calls.push(param_post_call_tkn(param_desc, param_index, ¶m_ident)); + tkn_func_args.push(param_ident.to_token_stream()); } - let mut func_call = quote! { - let call_result = (self.#func_ident)(#func_args); - }; - - if func.return_value.result { - func_call.extend(quote! { + let tkn_func_call_result_handling = if func.return_value.result { + quote! { if call_result.is_err() { return Err(()); } let call_result = call_result.unwrap(); - }); + } + } else { + quote! {} }; - if let Some(set_to) = set_to { + let tkn_func_call_set_return = if let Some(set_to) = set_to { let return_ty = func.return_value.ty.clone().unwrap(); - let result_wrap = expr_to_os_value("e! { call_result }, &return_ty, true); - func_call.extend(quote! { - let #set_to - }); - func_call.extend(quote! { = }); - func_call.extend(quote! {#result_wrap;}); - } + let tkn_result_unwrap = expr_to_os_value("e! { call_result }, &return_ty, true); + quote! { + let #set_to = #tkn_result_unwrap; + } + } else { + quote! {} + }; quote! { - #pre_call - #func_call - #post_call + // prepare parameters for native function call + #(#tkn_param_pre_calls)* + + let call_result = (self.#func_ident)(#(#tkn_func_args),*); + // handle Result if needed + #tkn_func_call_result_handling + // set return value if needed + #tkn_func_call_set_return + + // handle post call, like out parameters + #(#tkn_param_post_calls)* } } -fn gen_param_prep( +fn param_pre_call_tkn( param: &FuncArgumentDesc, param_index: usize, param_ident: &Ident, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { +) -> proc_macro2::TokenStream { let FuncParamType::PlatformType(param_ty) = ¶m.ty else { panic!("SelfType is not allowed here"); }; - let param_unwrap = expr_from_os_value("e! { params[#param_index]}, param_ty); - let mut pre_call = quote! {; - let #param_ident = #param_unwrap; - let mut #param_ident = #param_ident.clone().into(); - }; - if param.out_param { - pre_call.extend(quote! { + let tkn_param_unwrap = expr_from_os_value("e! { params[#param_index]}, param_ty); + let tkn_decouple_param = if param.out_param { + quote! { + let mut #param_ident = #param_ident.clone().into(); let #param_ident = &mut #param_ident; - }); + } + } else { + quote! { + let #param_ident = #param_ident.clone().into(); + } + }; + + quote! { + // extract from given params by index, storing as ref + let #param_ident = #tkn_param_unwrap; + // store as owned value with needed conversions from ParamValue to target Rust type + // if out param, make it a mutable reference + #tkn_decouple_param } +} - let post_call = if !param.out_param { - quote! {} - } else { - let param_wrap = expr_to_os_value(¶m_ident.to_token_stream(), param_ty, false); - let mut q = quote! { - params[#param_index] - }; - q.extend(quote! { = }); - q.extend(quote! { #param_wrap; }); - q +fn param_post_call_tkn( + param: &FuncArgumentDesc, + param_index: usize, + param_ident: &Ident, +) -> proc_macro2::TokenStream { + let FuncParamType::PlatformType(param_ty) = ¶m.ty else { + panic!("SelfType is not allowed here"); }; - (pre_call, post_call) + if !param.out_param { + return quote! {}; + } + + let tkn_param_wrap = expr_to_os_value(¶m_ident.to_token_stream(), param_ty, false); + + quote! { + params[#param_index] = #tkn_param_wrap; + } } diff --git a/native_api_1c_macro/src/derive_addin/functions/mod.rs b/native_api_1c_macro/src/derive_addin/functions/mod.rs index d35befb..469d0fb 100644 --- a/native_api_1c_macro/src/derive_addin/functions/mod.rs +++ b/native_api_1c_macro/src/derive_addin/functions/mod.rs @@ -4,6 +4,8 @@ use darling::FromMeta; use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; +use crate::derive_addin::constants::ANY_TYPE; + use super::{ constants::{BLOB_TYPE, BOOL_TYPE, DATE_TYPE, F64_TYPE, I32_TYPE, STRING_TYPE}, parsers::ParamType, @@ -104,6 +106,7 @@ impl TryFrom<&str> for FuncParamType { STRING_TYPE => Ok(FuncParamType::PlatformType(ParamType::String)), DATE_TYPE => Ok(FuncParamType::PlatformType(ParamType::Date)), BLOB_TYPE => Ok(FuncParamType::PlatformType(ParamType::Blob)), + ANY_TYPE => Ok(FuncParamType::PlatformType(ParamType::Any)), _ => Err(()), } } @@ -132,6 +135,9 @@ impl ToTokens for FuncParamType { ParamType::Blob => { quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Blob } } + ParamType::Any => { + quote! { native_api_1c::native_api_1c_core::interface::ParamValue } + } }, } } diff --git a/native_api_1c_macro/src/derive_addin/functions/parse.rs b/native_api_1c_macro/src/derive_addin/functions/parse.rs index b0428ad..01c6e3b 100644 --- a/native_api_1c_macro/src/derive_addin/functions/parse.rs +++ b/native_api_1c_macro/src/derive_addin/functions/parse.rs @@ -151,6 +151,7 @@ impl TryFrom for FuncArgumentDesc { ParamType::String => true, ParamType::Date => false, ParamType::Blob => false, + ParamType::Any => true, }, }; diff --git a/native_api_1c_macro/src/derive_addin/parsers.rs b/native_api_1c_macro/src/derive_addin/parsers.rs index 1875034..d85a94b 100644 --- a/native_api_1c_macro/src/derive_addin/parsers.rs +++ b/native_api_1c_macro/src/derive_addin/parsers.rs @@ -2,6 +2,8 @@ use darling::FromMeta; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; +use crate::derive_addin::constants::ANY_TYPE; + use super::constants::{BLOB_TYPE, BOOL_TYPE, DATE_TYPE, F64_TYPE, I32_TYPE, STRING_TYPE}; #[derive(Clone, Debug, PartialEq)] @@ -12,6 +14,7 @@ pub enum ParamType { String, Date, Blob, + Any, } const META_TYPE_ERR: &str = "expected string literal or path"; @@ -51,6 +54,7 @@ impl TryFrom<&str> for ParamType { STRING_TYPE => Ok(ParamType::String), DATE_TYPE => Ok(ParamType::Date), BLOB_TYPE => Ok(ParamType::Blob), + ANY_TYPE => Ok(ParamType::Any), _ => Err(()), } } @@ -77,6 +81,9 @@ impl ToTokens for ParamType { ParamType::Blob => { quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Blob } } + ParamType::Any => { + quote! { native_api_1c::native_api_1c_core::interface::ParamValue } + } } } } diff --git a/native_api_1c_macro/src/derive_addin/utils.rs b/native_api_1c_macro/src/derive_addin/utils.rs index 840eccd..9572816 100644 --- a/native_api_1c_macro/src/derive_addin/utils.rs +++ b/native_api_1c_macro/src/derive_addin/utils.rs @@ -50,16 +50,24 @@ pub fn expr_to_os_value( ty: &ParamType, string_nil: bool, ) -> proc_macro2::TokenStream { - let os_string_fn = if string_nil { - quote! {native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil} - } else { - quote! {native_api_1c::native_api_1c_core::ffi::string_utils::os_string} - }; match ty { - ParamType::String => quote! { + ParamType::String => { + let os_string_fn = if string_nil { + quote! {native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil} + } else { + quote! {native_api_1c::native_api_1c_core::ffi::string_utils::os_string} + }; + quote! { + { + let _ = "expr_to_os_value: specific case for String"; + #ty(#os_string_fn(&#expr.clone()).clone().into()) + } + } + } + ParamType::Any => quote! { { - let _ = "expr_to_os_value: specific case for String"; - #ty(#os_string_fn(&#expr.clone()).clone().into()) + let _ = "expr_to_os_value: specific case for Any"; + #expr.clone() } }, _ => quote! { @@ -75,7 +83,6 @@ pub fn expr_from_os_value(expr: &TokenStream, ty: &ParamType) -> proc_macro2::To match ty { ParamType::String => quote! { { - let _ = "expr_from_os_value: specific case for String"; match &#expr { #ty(val) => { Ok(native_api_1c::native_api_1c_core::ffi::string_utils::from_os_string(&val)) @@ -86,7 +93,6 @@ pub fn expr_from_os_value(expr: &TokenStream, ty: &ParamType) -> proc_macro2::To }, ParamType::Blob => quote! { { - let _ = "expr_from_os_value: specific case for Blob"; match &#expr { #ty(val) => { Ok(val) @@ -95,9 +101,11 @@ pub fn expr_from_os_value(expr: &TokenStream, ty: &ParamType) -> proc_macro2::To }?.clone() } }, + ParamType::Any => quote! { + &#expr + }, _ => quote! { { - let _ = "expr_from_os_value: generic case"; match #expr { #ty(val) => { Ok(val) diff --git a/native_api_1c_macro/src/extern_functions/parse.rs b/native_api_1c_macro/src/extern_functions/parse.rs index 572fbe1..d94f1c5 100644 --- a/native_api_1c_macro/src/extern_functions/parse.rs +++ b/native_api_1c_macro/src/extern_functions/parse.rs @@ -38,6 +38,7 @@ struct ExternAddInComponentMeta { #[derive(Debug)] pub struct ExternAddInComponentDesc { + #[allow(dead_code)] pub name_override: Option, pub init_tkn: TokenStream, } diff --git a/native_api_1c_macro/tests/interface/functions.rs b/native_api_1c_macro/tests/interface/functions.rs index 9348b99..6b86f6a 100644 --- a/native_api_1c_macro/tests/interface/functions.rs +++ b/native_api_1c_macro/tests/interface/functions.rs @@ -22,10 +22,13 @@ const PROCEDURE_NAME_RU: &str = "Процедура"; const OUT_FUNCTION_NAME_EN: &str = "OutFunction"; const OUT_FUNCTION_NAME_RU: &str = "ВыводФункция"; +const ANY_ARG_NAME_EN: &str = "AnyArg"; +const ANY_ARG_NAME_RU: &str = "ЛюбойАргумент"; + const INVALID_NAME: &str = "Invalid"; #[derive(AddIn)] -struct TestAddIn { +struct TestAddInFixture { #[add_in_con] connection: Arc>, @@ -45,11 +48,16 @@ struct TestAddIn { #[add_in_func(name = OUT_FUNCTION_NAME_EN, name_ru = OUT_FUNCTION_NAME_RU)] #[arg(ty = Str, as_out, default = OUT_STR)] pub out_function: fn(&mut String), + + #[add_in_func(name = ANY_ARG_NAME_EN, name_ru = ANY_ARG_NAME_RU)] + #[arg(ty = Any)] + #[returns(ty = Any)] + pub any_arg_func: fn(&Self, ParamValue) -> ParamValue, } #[fixture] -fn add_in() -> TestAddIn { - TestAddIn { +fn add_in() -> TestAddInFixture { + TestAddInFixture { connection: Arc::new(None), storage: 0, function: |addin, a, b| Ok(a + b + addin.storage), @@ -59,12 +67,13 @@ fn add_in() -> TestAddIn { out_function: |out_str| { *out_str = format!("Hello, {out_str}!"); }, + any_arg_func: |_, arg| arg, } } #[rstest] -fn test_get_n_methods(add_in: TestAddIn) { - assert_eq!(add_in.get_n_methods(), 3) +fn test_get_n_methods(add_in: TestAddInFixture) { + assert_eq!(add_in.get_n_methods(), 4) } #[rstest] @@ -75,9 +84,7 @@ fn test_get_n_methods(add_in: TestAddIn) { #[case(OUT_FUNCTION_NAME_EN, Some(2))] #[case(OUT_FUNCTION_NAME_RU, Some(2))] #[case(INVALID_NAME, None)] -fn test_find_method(add_in: TestAddIn, #[case] name: &str, #[case] expected: Option) { - use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; - +fn test_find_method(add_in: TestAddInFixture, #[case] name: &str, #[case] expected: Option) { assert_eq!(add_in.find_method(&os_string_nil(name)), expected); } @@ -91,15 +98,13 @@ fn test_find_method(add_in: TestAddIn, #[case] name: &str, #[case] expected: Opt #[case(2, 0, Some(OUT_FUNCTION_NAME_EN))] #[case(2, 1, Some(OUT_FUNCTION_NAME_RU))] #[case(2, 42, Some(OUT_FUNCTION_NAME_RU))] -#[case(3, 0, None)] +#[case(42, 0, None)] fn test_get_method_name( - add_in: TestAddIn, + add_in: TestAddInFixture, #[case] method_i: usize, #[case] alias_i: usize, #[case] expected: Option<&str>, ) { - use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; - assert_eq!( add_in.get_method_name(method_i, alias_i), expected.map(os_string_nil) @@ -110,8 +115,9 @@ fn test_get_method_name( #[case(0, 2)] #[case(1, 2)] #[case(2, 1)] -#[case(3, 0)] -fn test_get_n_params(add_in: TestAddIn, #[case] method_i: usize, #[case] n_params: usize) { +#[case(3, 1)] +#[case(42, 0)] +fn test_get_n_params(add_in: TestAddInFixture, #[case] method_i: usize, #[case] n_params: usize) { assert_eq!(add_in.get_n_params(method_i), n_params); } @@ -126,7 +132,7 @@ fn test_get_n_params(add_in: TestAddIn, #[case] method_i: usize, #[case] n_param #[case(2, 42, None)] #[case(3, 0, None)] fn test_get_param_def_value( - add_in: TestAddIn, + add_in: TestAddInFixture, #[case] method_i: usize, #[case] param_i: usize, #[case] expected: Option, @@ -138,13 +144,14 @@ fn test_get_param_def_value( #[case(0, true)] #[case(1, false)] #[case(2, false)] -#[case(3, false)] -fn test_has_ret_val(add_in: TestAddIn, #[case] method_i: usize, #[case] has_ret_val: bool) { +#[case(3, true)] +#[case(42, false)] +fn test_has_ret_val(add_in: TestAddInFixture, #[case] method_i: usize, #[case] has_ret_val: bool) { assert_eq!(add_in.has_ret_val(method_i), has_ret_val); } #[rstest] -fn test_call_function(mut add_in: TestAddIn) { +fn test_call_function(mut add_in: TestAddInFixture) { let a = ParamValue::I32(1); let b = ParamValue::I32(2); let mut params = ParamValues::new(vec![a, b]); @@ -158,7 +165,7 @@ fn test_call_function(mut add_in: TestAddIn) { } #[rstest] -fn test_call_procedure(mut add_in: TestAddIn) { +fn test_call_procedure(mut add_in: TestAddInFixture) { let a = ParamValue::I32(1); let b = ParamValue::I32(2); let mut params = ParamValues::new(vec![a, b]); @@ -172,7 +179,7 @@ fn test_call_procedure(mut add_in: TestAddIn) { } #[rstest] -fn test_call_out_function(mut add_in: TestAddIn) { +fn test_call_out_function(mut add_in: TestAddInFixture) { let out_str = os_string("1C"); let mut params = ParamValues::new(vec![ParamValue::String(out_str)]); @@ -183,3 +190,16 @@ fn test_call_out_function(mut add_in: TestAddIn) { assert!(result.is_ok()); assert_eq!(params[0], ParamValue::String(os_string("Hello, 1C!"))); } + +#[rstest] +#[case(ParamValue::I32(42))] +#[case(ParamValue::String(os_string("Test")))] +#[case(ParamValue::Bool(true))] +fn test_call_any_arg_function(mut add_in: TestAddInFixture, #[case] value: ParamValue) { + let params = ParamValues::new(vec![value.clone()]); + let func = add_in.find_method(&os_string_nil(ANY_ARG_NAME_EN)).unwrap(); + + let result = add_in.call_as_func(func, &mut params.clone()); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), value); +} diff --git a/native_api_1c_macro/tests/trybuild/to_build/props.rs b/native_api_1c_macro/tests/trybuild/to_build/props.rs index 1a9256e..9d39d7b 100644 --- a/native_api_1c_macro/tests/trybuild/to_build/props.rs +++ b/native_api_1c_macro/tests/trybuild/to_build/props.rs @@ -67,8 +67,11 @@ impl MyAddIn { bool_prop_rw: false, bool_prop_r: false, bool_prop_w: false, + #[allow(deprecated)] date_prop_rw: chrono::NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), + #[allow(deprecated)] date_prop_r: chrono::NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), + #[allow(deprecated)] date_prop_w: chrono::NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), blob_prop_rw: Vec::new(), blob_prop_r: Vec::new(), diff --git a/sample_addin_rs/src/lib.rs b/sample_addin_rs/src/lib.rs index 15ed9fc..132e4dc 100644 --- a/sample_addin_rs/src/lib.rs +++ b/sample_addin_rs/src/lib.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use native_api_1c::{ - native_api_1c_core::ffi::connection::Connection, + native_api_1c_core::{ffi::connection::Connection, interface::ParamValue}, native_api_1c_macro::{extern_functions, AddIn}, }; @@ -37,6 +37,13 @@ pub struct SampleAddIn { #[add_in_func(name = "MyProcedure", name_ru = "МояПроцедура")] pub my_procedure: fn(&mut Self), + /// Function that accepts Any types + #[add_in_func(name = "CompareAny", name_ru = "СравнитьЛюбое")] + #[arg(ty = Any)] + #[arg(ty = Any)] + #[returns(ty = Bool)] + pub compare_any: fn(ParamValue, ParamValue) -> bool, + /// Private field, not visible from 1C private_field: i32, } @@ -50,6 +57,7 @@ impl Default for SampleAddIn { my_function: Self::my_function_inner, my_procedure: Self::my_procedure_inner, private_field: 100, + compare_any: Self::compare_any, } } } @@ -66,6 +74,10 @@ impl SampleAddIn { fn my_procedure_inner(&mut self) { self.protected_prop += 10; } + + fn compare_any(arg1: ParamValue, arg2: ParamValue) -> bool { + arg1 == arg2 + } } extern_functions! {