From a390f04a35331456a3e8deecca158e4c1459d628 Mon Sep 17 00:00:00 2001 From: Maksim Kozlov Date: Tue, 9 Dec 2025 18:11:36 +0400 Subject: [PATCH 1/4] feat: support for any type for functions --- Cargo.toml | 1 + native_api_1c_core/src/ffi/mod.rs | 4 +- native_api_1c_core/src/interface.rs | 2 +- .../src/derive_addin/constants.rs | 3 + .../functions/collectors/call_as_func.rs | 20 +- .../functions/collectors/call_as_proc.rs | 12 +- .../src/derive_addin/functions/generate.rs | 133 ++-- .../src/derive_addin/functions/mod.rs | 6 + .../src/derive_addin/functions/parse.rs | 1 + .../src/derive_addin/parsers.rs | 7 + native_api_1c_macro/src/derive_addin/utils.rs | 6 +- .../src/extern_functions/parse.rs | 1 + .../tests/trybuild/to_build/props.rs | 3 + sample_addin_rs/src/expanded.rs | 614 ++++++++++++++++++ sample_addin_rs/src/lib.rs | 14 +- 15 files changed, 745 insertions(+), 82 deletions(-) create mode 100644 sample_addin_rs/src/expanded.rs 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/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/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..9570b00 100644 --- a/native_api_1c_macro/src/derive_addin/utils.rs +++ b/native_api_1c_macro/src/derive_addin/utils.rs @@ -75,7 +75,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 +85,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 +93,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/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/expanded.rs b/sample_addin_rs/src/expanded.rs new file mode 100644 index 0000000..da68530 --- /dev/null +++ b/sample_addin_rs/src/expanded.rs @@ -0,0 +1,614 @@ +#![feature(prelude_import)] +#[macro_use] +extern crate std; +#[prelude_import] +use std::prelude::rust_2021::*; +use std::sync::Arc; +use native_api_1c::{ + native_api_1c_core::{ffi::connection::Connection, interface::ParamValue}, + native_api_1c_macro::{extern_functions, AddIn}, +}; +pub struct SampleAddIn { + /// connection with 1C, used for calling events + /// Arc is used to allow multiple threads to access the connection + #[add_in_con] + connection: Arc>, + /// Property, readable and writable from 1C + #[add_in_prop( + ty = Int, + name = "MyProp", + name_ru = "МоеСвойство", + readable, + writable + )] + pub some_prop: i32, + /// Property, readable from 1C but not writable + #[add_in_prop( + ty = Int, + name = "ProtectedProp", + name_ru = "ЗащищенноеСвойство", + readable + )] + pub protected_prop: i32, + /// Function, taking one or two arguments and returning a result + /// In 1C it can be called as: + /// ```bsl + /// CompObj.MyFunction(10, 15); // 2nd arg = 15 + /// CompObj.MyFunction(10); // 2nd arg = 12 (default value) + /// ``` + /// If function returns an error, but does not panic, then 1C will throw an exception + #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] + #[arg(ty = Int)] + #[arg(ty = Int, default = 12)] + #[returns(ty = Int, result)] + pub my_function: fn(&Self, i32, i64) -> Result, + /// Function, taking no arguments and returning nothing + #[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, +} +impl native_api_1c::native_api_1c_core::interface::AddInWrapper for SampleAddIn { + fn init( + &mut self, + interface: &'static native_api_1c::native_api_1c_core::ffi::connection::Connection, + ) -> bool { + self.connection = std::sync::Arc::new(Some(interface)); + true + } + fn get_info(&self) -> u16 { + 2000 + } + fn done(&mut self) {} + fn register_extension_as(&mut self) -> &[u16] { + &{ + const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8: &str = "SampleAddIn"; + const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN: usize = ::utf16_lit::internals::length_as_utf16( + ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8, + ) + 1; + const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16: [u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN] = { + let mut buffer = [0u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN]; + let mut bytes = ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8 + .as_bytes(); + let mut i = 0; + while let Some((ch, rest)) = ::utf16_lit::internals::next_code_point( + bytes, + ) { + bytes = rest; + if ch & 0xFFFF == ch { + buffer[i] = ch as u16; + i += 1; + } else { + let code = ch - 0x1_0000; + buffer[i] = 0xD800 | ((code >> 10) as u16); + buffer[i + 1] = 0xDC00 | ((code as u16) & 0x3FF); + i += 2; + } + } + buffer + }; + ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16 + } + } + fn find_prop(&self, name: &[u16]) -> Option { + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil("MyProp") + == name + { + return Some(0usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МоеСвойство", + ) == name + { + return Some(0usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "ProtectedProp", + ) == name + { + return Some(1usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "ЗащищенноеСвойство", + ) == name + { + return Some(1usize); + } + None + } + fn get_n_props(&self) -> usize { + 2usize + } + fn get_prop_name(&self, num: usize, alias: usize) -> Option> { + if num == 0usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyProp", + ) + .into(), + ); + } + if num == 0usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МоеСвойство", + ) + .into(), + ); + } + if num == 1usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "ProtectedProp", + ) + .into(), + ); + } + if num == 1usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "ЗащищенноеСвойство", + ) + .into(), + ); + } + None + } + fn is_prop_readable(&self, num: usize) -> bool { + if num == 0usize { + return true; + } + if num == 1usize { + return true; + } + false + } + fn is_prop_writable(&self, num: usize) -> bool { + if num == 0usize { + return true; + } + if num == 1usize { + return false; + } + false + } + fn get_prop_val( + &self, + num: usize, + ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult< + native_api_1c::native_api_1c_core::interface::ParamValue, + > { + if num == 0usize { + return Ok({ + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + self.some_prop.clone().into(), + ) + }); + } + if num == 1usize { + return Ok({ + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + self.protected_prop.clone().into(), + ) + }); + } + return Err(()); + } + fn set_prop_val( + &mut self, + num: usize, + val: native_api_1c::native_api_1c_core::interface::ParamValue, + ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult<()> { + if num == 0usize { + self.some_prop = { + match val { + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + val, + ) => Ok(val), + _ => Err(()), + }? + .clone() + } + .into(); + return Ok(()); + } + return Err(()); + } + fn find_method(&self, name: &[u16]) -> Option { + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyFunction", + ) == name + { + return Some(0usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояФункция", + ) == name + { + return Some(0usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyProcedure", + ) == name + { + return Some(1usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояПроцедура", + ) == name + { + return Some(1usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "CompareAny", + ) == name + { + return Some(2usize); + } + if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "СравнитьЛюбое", + ) == name + { + return Some(2usize); + } + None + } + fn get_method_name(&self, num: usize, alias: usize) -> Option> { + if num == 0usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyFunction", + ) + .into(), + ); + } + if num == 0usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояФункция", + ) + .into(), + ); + } + if num == 0usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyFunction", + ) + .into(), + ); + } + if num == 0usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояФункция", + ) + .into(), + ); + } + if num == 1usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyProcedure", + ) + .into(), + ); + } + if num == 1usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояПроцедура", + ) + .into(), + ); + } + if num == 0usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyFunction", + ) + .into(), + ); + } + if num == 0usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояФункция", + ) + .into(), + ); + } + if num == 0usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyFunction", + ) + .into(), + ); + } + if num == 0usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояФункция", + ) + .into(), + ); + } + if num == 1usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "MyProcedure", + ) + .into(), + ); + } + if num == 1usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "МояПроцедура", + ) + .into(), + ); + } + if num == 2usize && alias == 0 { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "CompareAny", + ) + .into(), + ); + } + if num == 2usize { + return Some( + native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( + "СравнитьЛюбое", + ) + .into(), + ); + } + None + } + fn get_n_methods(&self) -> usize { + 3usize + } + fn get_n_params(&self, num: usize) -> usize { + if num == 0usize { + return 2usize; + } + if num == 1usize { + return 0usize; + } + if num == 2usize { + return 2usize; + } + 0 + } + fn has_ret_val(&self, method_num: usize) -> bool { + if method_num == 0usize { + return true; + } + if method_num == 1usize { + return false; + } + if method_num == 2usize { + return true; + } + false + } + fn call_as_proc( + &mut self, + method_num: usize, + params: &mut native_api_1c::native_api_1c_core::interface::ParamValues, + ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult<()> { + if method_num == 0usize { + let param_1 = { + match params[0usize] { + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + val, + ) => Ok(val), + _ => Err(()), + }? + .clone() + }; + let param_1 = param_1.clone().into(); + let param_2 = { + match params[1usize] { + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + val, + ) => Ok(val), + _ => Err(()), + }? + .clone() + }; + let param_2 = param_2.clone().into(); + let call_result = (self.my_function)(self, param_1, param_2); + if call_result.is_err() { + return Err(()); + } + let call_result = call_result.unwrap(); + } + if method_num == 1usize { + let call_result = (self.my_procedure)(self); + } + if method_num == 2usize { + let param_1 = ¶ms[0usize]; + let param_1 = param_1.clone().into(); + let param_2 = ¶ms[1usize]; + let param_2 = param_2.clone().into(); + let call_result = (self.compare_any)(param_1, param_2); + } + } + fn call_as_func( + &mut self, + method_num: usize, + params: &mut native_api_1c::native_api_1c_core::interface::ParamValues, + ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult< + native_api_1c::native_api_1c_core::interface::ParamValue, + > { + if method_num == 0usize { + let param_1 = { + match params[0usize] { + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + val, + ) => Ok(val), + _ => Err(()), + }? + .clone() + }; + let param_1 = param_1.clone().into(); + let param_2 = { + match params[1usize] { + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + val, + ) => Ok(val), + _ => Err(()), + }? + .clone() + }; + let param_2 = param_2.clone().into(); + let call_result = (self.my_function)(self, param_1, param_2); + if call_result.is_err() { + return Err(()); + } + let call_result = call_result.unwrap(); + let val = { + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + call_result.clone().into(), + ) + }; + return Ok(val); + } + if method_num == 2usize { + let param_1 = ¶ms[0usize]; + let param_1 = param_1.clone().into(); + let param_2 = ¶ms[1usize]; + let param_2 = param_2.clone().into(); + let call_result = (self.compare_any)(param_1, param_2); + let val = { + native_api_1c::native_api_1c_core::interface::ParamValue::Bool( + call_result.clone().into(), + ) + }; + return Ok(val); + } + Err(()) + } + fn get_param_def_value( + &self, + method_num: usize, + param_num: usize, + ) -> Option { + if method_num == 0usize { + if param_num == 1usize { + return Some({ + native_api_1c::native_api_1c_core::interface::ParamValue::I32( + 12.clone().into(), + ) + }); + } + return None; + } + if method_num == 1usize { + return None; + } + if method_num == 2usize { + return None; + } + None + } + fn set_locale(&mut self, loc: &[u16]) {} + fn set_user_interface_language_code(&mut self, lang: &[u16]) {} +} +impl Default for SampleAddIn { + fn default() -> Self { + Self { + connection: Arc::new(None), + some_prop: 0, + protected_prop: 50, + my_function: Self::my_function_inner, + my_procedure: Self::my_procedure_inner, + private_field: 100, + compare_any: Self::compare_any, + } + } +} +impl SampleAddIn { + fn my_function_inner(&self, arg: i32, arg_maybe_default: i64) -> Result { + Ok( + self.protected_prop + self.some_prop + arg + self.private_field + + arg_maybe_default as i32, + ) + } + fn my_procedure_inner(&mut self) { + self.protected_prop += 10; + } + fn compare_any(arg1: ParamValue, arg2: ParamValue) -> bool { + arg1 == arg2 + } +} +pub static mut PLATFORM_CAPABILITIES: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new( + -1, +); +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn GetAttachType() -> native_api_1c::native_api_1c_core::ffi::AttachType { + native_api_1c::native_api_1c_core::ffi::AttachType::Any +} +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn DestroyObject( + component: *mut *mut std::ffi::c_void, +) -> std::ffi::c_long { + native_api_1c::native_api_1c_core::ffi::destroy_component(component) +} +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn GetClassObject( + name: *const u16, + component: *mut *mut std::ffi::c_void, +) -> std::ffi::c_long { + match *name as u8 { + 48u8 => { + let add_in = SampleAddIn::default(); + native_api_1c::native_api_1c_core::ffi::create_component(component, add_in) + } + _ => 0, + } +} +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn GetClassNames() -> *const u16 { + { + const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8: &str = "0"; + const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN: usize = ::utf16_lit::internals::length_as_utf16( + ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8, + ) + 1; + const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16: [u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN] = { + let mut buffer = [0u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN]; + let mut bytes = ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8 + .as_bytes(); + let mut i = 0; + while let Some((ch, rest)) = ::utf16_lit::internals::next_code_point(bytes) { + bytes = rest; + if ch & 0xFFFF == ch { + buffer[i] = ch as u16; + i += 1; + } else { + let code = ch - 0x1_0000; + buffer[i] = 0xD800 | ((code >> 10) as u16); + buffer[i + 1] = 0xDC00 | ((code as u16) & 0x3FF); + i += 2; + } + } + buffer + }; + ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16 + } + .as_ptr() +} 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! { From 564591a5f288f279fd4d683853f27fd9d2664bfe Mon Sep 17 00:00:00 2001 From: Maksim Kozlov Date: Tue, 9 Dec 2025 22:33:30 +0400 Subject: [PATCH 2/4] added to readme and test coverage --- README.md | 20 +- native_api_1c_macro/src/derive_addin/utils.rs | 24 +- .../tests/interface/functions.rs | 56 +- sample_addin_rs/src/expanded.rs | 614 ------------------ 4 files changed, 73 insertions(+), 641 deletions(-) delete mode 100644 sample_addin_rs/src/expanded.rs 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_macro/src/derive_addin/utils.rs b/native_api_1c_macro/src/derive_addin/utils.rs index 9570b00..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! { diff --git a/native_api_1c_macro/tests/interface/functions.rs b/native_api_1c_macro/tests/interface/functions.rs index 9348b99..ebf3d8c 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,7 +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) { +fn test_find_method(add_in: TestAddInFixture, #[case] name: &str, #[case] expected: Option) { use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; assert_eq!(add_in.find_method(&os_string_nil(name)), expected); @@ -91,9 +100,9 @@ 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>, @@ -110,8 +119,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 +136,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 +148,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 +169,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 +183,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 +194,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(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/sample_addin_rs/src/expanded.rs b/sample_addin_rs/src/expanded.rs deleted file mode 100644 index da68530..0000000 --- a/sample_addin_rs/src/expanded.rs +++ /dev/null @@ -1,614 +0,0 @@ -#![feature(prelude_import)] -#[macro_use] -extern crate std; -#[prelude_import] -use std::prelude::rust_2021::*; -use std::sync::Arc; -use native_api_1c::{ - native_api_1c_core::{ffi::connection::Connection, interface::ParamValue}, - native_api_1c_macro::{extern_functions, AddIn}, -}; -pub struct SampleAddIn { - /// connection with 1C, used for calling events - /// Arc is used to allow multiple threads to access the connection - #[add_in_con] - connection: Arc>, - /// Property, readable and writable from 1C - #[add_in_prop( - ty = Int, - name = "MyProp", - name_ru = "МоеСвойство", - readable, - writable - )] - pub some_prop: i32, - /// Property, readable from 1C but not writable - #[add_in_prop( - ty = Int, - name = "ProtectedProp", - name_ru = "ЗащищенноеСвойство", - readable - )] - pub protected_prop: i32, - /// Function, taking one or two arguments and returning a result - /// In 1C it can be called as: - /// ```bsl - /// CompObj.MyFunction(10, 15); // 2nd arg = 15 - /// CompObj.MyFunction(10); // 2nd arg = 12 (default value) - /// ``` - /// If function returns an error, but does not panic, then 1C will throw an exception - #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] - #[arg(ty = Int)] - #[arg(ty = Int, default = 12)] - #[returns(ty = Int, result)] - pub my_function: fn(&Self, i32, i64) -> Result, - /// Function, taking no arguments and returning nothing - #[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, -} -impl native_api_1c::native_api_1c_core::interface::AddInWrapper for SampleAddIn { - fn init( - &mut self, - interface: &'static native_api_1c::native_api_1c_core::ffi::connection::Connection, - ) -> bool { - self.connection = std::sync::Arc::new(Some(interface)); - true - } - fn get_info(&self) -> u16 { - 2000 - } - fn done(&mut self) {} - fn register_extension_as(&mut self) -> &[u16] { - &{ - const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8: &str = "SampleAddIn"; - const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN: usize = ::utf16_lit::internals::length_as_utf16( - ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8, - ) + 1; - const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16: [u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN] = { - let mut buffer = [0u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN]; - let mut bytes = ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8 - .as_bytes(); - let mut i = 0; - while let Some((ch, rest)) = ::utf16_lit::internals::next_code_point( - bytes, - ) { - bytes = rest; - if ch & 0xFFFF == ch { - buffer[i] = ch as u16; - i += 1; - } else { - let code = ch - 0x1_0000; - buffer[i] = 0xD800 | ((code >> 10) as u16); - buffer[i + 1] = 0xDC00 | ((code as u16) & 0x3FF); - i += 2; - } - } - buffer - }; - ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16 - } - } - fn find_prop(&self, name: &[u16]) -> Option { - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil("MyProp") - == name - { - return Some(0usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МоеСвойство", - ) == name - { - return Some(0usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "ProtectedProp", - ) == name - { - return Some(1usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "ЗащищенноеСвойство", - ) == name - { - return Some(1usize); - } - None - } - fn get_n_props(&self) -> usize { - 2usize - } - fn get_prop_name(&self, num: usize, alias: usize) -> Option> { - if num == 0usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyProp", - ) - .into(), - ); - } - if num == 0usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МоеСвойство", - ) - .into(), - ); - } - if num == 1usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "ProtectedProp", - ) - .into(), - ); - } - if num == 1usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "ЗащищенноеСвойство", - ) - .into(), - ); - } - None - } - fn is_prop_readable(&self, num: usize) -> bool { - if num == 0usize { - return true; - } - if num == 1usize { - return true; - } - false - } - fn is_prop_writable(&self, num: usize) -> bool { - if num == 0usize { - return true; - } - if num == 1usize { - return false; - } - false - } - fn get_prop_val( - &self, - num: usize, - ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult< - native_api_1c::native_api_1c_core::interface::ParamValue, - > { - if num == 0usize { - return Ok({ - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - self.some_prop.clone().into(), - ) - }); - } - if num == 1usize { - return Ok({ - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - self.protected_prop.clone().into(), - ) - }); - } - return Err(()); - } - fn set_prop_val( - &mut self, - num: usize, - val: native_api_1c::native_api_1c_core::interface::ParamValue, - ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult<()> { - if num == 0usize { - self.some_prop = { - match val { - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - val, - ) => Ok(val), - _ => Err(()), - }? - .clone() - } - .into(); - return Ok(()); - } - return Err(()); - } - fn find_method(&self, name: &[u16]) -> Option { - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyFunction", - ) == name - { - return Some(0usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояФункция", - ) == name - { - return Some(0usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyProcedure", - ) == name - { - return Some(1usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояПроцедура", - ) == name - { - return Some(1usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "CompareAny", - ) == name - { - return Some(2usize); - } - if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "СравнитьЛюбое", - ) == name - { - return Some(2usize); - } - None - } - fn get_method_name(&self, num: usize, alias: usize) -> Option> { - if num == 0usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyFunction", - ) - .into(), - ); - } - if num == 0usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояФункция", - ) - .into(), - ); - } - if num == 0usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyFunction", - ) - .into(), - ); - } - if num == 0usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояФункция", - ) - .into(), - ); - } - if num == 1usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyProcedure", - ) - .into(), - ); - } - if num == 1usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояПроцедура", - ) - .into(), - ); - } - if num == 0usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyFunction", - ) - .into(), - ); - } - if num == 0usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояФункция", - ) - .into(), - ); - } - if num == 0usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyFunction", - ) - .into(), - ); - } - if num == 0usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояФункция", - ) - .into(), - ); - } - if num == 1usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "MyProcedure", - ) - .into(), - ); - } - if num == 1usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "МояПроцедура", - ) - .into(), - ); - } - if num == 2usize && alias == 0 { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "CompareAny", - ) - .into(), - ); - } - if num == 2usize { - return Some( - native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( - "СравнитьЛюбое", - ) - .into(), - ); - } - None - } - fn get_n_methods(&self) -> usize { - 3usize - } - fn get_n_params(&self, num: usize) -> usize { - if num == 0usize { - return 2usize; - } - if num == 1usize { - return 0usize; - } - if num == 2usize { - return 2usize; - } - 0 - } - fn has_ret_val(&self, method_num: usize) -> bool { - if method_num == 0usize { - return true; - } - if method_num == 1usize { - return false; - } - if method_num == 2usize { - return true; - } - false - } - fn call_as_proc( - &mut self, - method_num: usize, - params: &mut native_api_1c::native_api_1c_core::interface::ParamValues, - ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult<()> { - if method_num == 0usize { - let param_1 = { - match params[0usize] { - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - val, - ) => Ok(val), - _ => Err(()), - }? - .clone() - }; - let param_1 = param_1.clone().into(); - let param_2 = { - match params[1usize] { - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - val, - ) => Ok(val), - _ => Err(()), - }? - .clone() - }; - let param_2 = param_2.clone().into(); - let call_result = (self.my_function)(self, param_1, param_2); - if call_result.is_err() { - return Err(()); - } - let call_result = call_result.unwrap(); - } - if method_num == 1usize { - let call_result = (self.my_procedure)(self); - } - if method_num == 2usize { - let param_1 = ¶ms[0usize]; - let param_1 = param_1.clone().into(); - let param_2 = ¶ms[1usize]; - let param_2 = param_2.clone().into(); - let call_result = (self.compare_any)(param_1, param_2); - } - } - fn call_as_func( - &mut self, - method_num: usize, - params: &mut native_api_1c::native_api_1c_core::interface::ParamValues, - ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult< - native_api_1c::native_api_1c_core::interface::ParamValue, - > { - if method_num == 0usize { - let param_1 = { - match params[0usize] { - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - val, - ) => Ok(val), - _ => Err(()), - }? - .clone() - }; - let param_1 = param_1.clone().into(); - let param_2 = { - match params[1usize] { - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - val, - ) => Ok(val), - _ => Err(()), - }? - .clone() - }; - let param_2 = param_2.clone().into(); - let call_result = (self.my_function)(self, param_1, param_2); - if call_result.is_err() { - return Err(()); - } - let call_result = call_result.unwrap(); - let val = { - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - call_result.clone().into(), - ) - }; - return Ok(val); - } - if method_num == 2usize { - let param_1 = ¶ms[0usize]; - let param_1 = param_1.clone().into(); - let param_2 = ¶ms[1usize]; - let param_2 = param_2.clone().into(); - let call_result = (self.compare_any)(param_1, param_2); - let val = { - native_api_1c::native_api_1c_core::interface::ParamValue::Bool( - call_result.clone().into(), - ) - }; - return Ok(val); - } - Err(()) - } - fn get_param_def_value( - &self, - method_num: usize, - param_num: usize, - ) -> Option { - if method_num == 0usize { - if param_num == 1usize { - return Some({ - native_api_1c::native_api_1c_core::interface::ParamValue::I32( - 12.clone().into(), - ) - }); - } - return None; - } - if method_num == 1usize { - return None; - } - if method_num == 2usize { - return None; - } - None - } - fn set_locale(&mut self, loc: &[u16]) {} - fn set_user_interface_language_code(&mut self, lang: &[u16]) {} -} -impl Default for SampleAddIn { - fn default() -> Self { - Self { - connection: Arc::new(None), - some_prop: 0, - protected_prop: 50, - my_function: Self::my_function_inner, - my_procedure: Self::my_procedure_inner, - private_field: 100, - compare_any: Self::compare_any, - } - } -} -impl SampleAddIn { - fn my_function_inner(&self, arg: i32, arg_maybe_default: i64) -> Result { - Ok( - self.protected_prop + self.some_prop + arg + self.private_field - + arg_maybe_default as i32, - ) - } - fn my_procedure_inner(&mut self) { - self.protected_prop += 10; - } - fn compare_any(arg1: ParamValue, arg2: ParamValue) -> bool { - arg1 == arg2 - } -} -pub static mut PLATFORM_CAPABILITIES: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new( - -1, -); -#[allow(non_snake_case)] -#[no_mangle] -pub extern "C" fn GetAttachType() -> native_api_1c::native_api_1c_core::ffi::AttachType { - native_api_1c::native_api_1c_core::ffi::AttachType::Any -} -#[allow(non_snake_case)] -#[no_mangle] -pub unsafe extern "C" fn DestroyObject( - component: *mut *mut std::ffi::c_void, -) -> std::ffi::c_long { - native_api_1c::native_api_1c_core::ffi::destroy_component(component) -} -#[allow(non_snake_case)] -#[no_mangle] -pub unsafe extern "C" fn GetClassObject( - name: *const u16, - component: *mut *mut std::ffi::c_void, -) -> std::ffi::c_long { - match *name as u8 { - 48u8 => { - let add_in = SampleAddIn::default(); - native_api_1c::native_api_1c_core::ffi::create_component(component, add_in) - } - _ => 0, - } -} -#[allow(non_snake_case)] -#[no_mangle] -pub extern "C" fn GetClassNames() -> *const u16 { - { - const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8: &str = "0"; - const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN: usize = ::utf16_lit::internals::length_as_utf16( - ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8, - ) + 1; - const ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16: [u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN] = { - let mut buffer = [0u16; ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_LEN]; - let mut bytes = ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF8 - .as_bytes(); - let mut i = 0; - while let Some((ch, rest)) = ::utf16_lit::internals::next_code_point(bytes) { - bytes = rest; - if ch & 0xFFFF == ch { - buffer[i] = ch as u16; - i += 1; - } else { - let code = ch - 0x1_0000; - buffer[i] = 0xD800 | ((code >> 10) as u16); - buffer[i + 1] = 0xDC00 | ((code as u16) & 0x3FF); - i += 2; - } - } - buffer - }; - ABC678_PREFIX_THAT_SHOULD_NEVER_CLASH_WITH_OUTER_SCOPE_UTF16 - } - .as_ptr() -} From 96954c4cab0dccd2f018c5377562827b54b1200d Mon Sep 17 00:00:00 2001 From: Maksim Kozlov Date: Wed, 10 Dec 2025 13:23:51 +0400 Subject: [PATCH 3/4] fixed missing zero in os_string_nil --- native_api_1c_core/src/ffi/string_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 24204171694d27a5ac46cd02562cfdcf8792626c Mon Sep 17 00:00:00 2001 From: Maksim Kozlov Date: Wed, 10 Dec 2025 13:27:15 +0400 Subject: [PATCH 4/4] fixed test using os_string without nil --- native_api_1c_macro/tests/interface/functions.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/native_api_1c_macro/tests/interface/functions.rs b/native_api_1c_macro/tests/interface/functions.rs index ebf3d8c..6b86f6a 100644 --- a/native_api_1c_macro/tests/interface/functions.rs +++ b/native_api_1c_macro/tests/interface/functions.rs @@ -85,8 +85,6 @@ fn test_get_n_methods(add_in: TestAddInFixture) { #[case(OUT_FUNCTION_NAME_RU, Some(2))] #[case(INVALID_NAME, None)] fn test_find_method(add_in: TestAddInFixture, #[case] name: &str, #[case] expected: Option) { - use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; - assert_eq!(add_in.find_method(&os_string_nil(name)), expected); } @@ -107,8 +105,6 @@ fn test_get_method_name( #[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) @@ -201,7 +197,7 @@ fn test_call_out_function(mut add_in: TestAddInFixture) { #[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(ANY_ARG_NAME_EN)).unwrap(); + 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());