├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── README.md ├── native_api_1c ├── Cargo.toml ├── LICENSE └── src │ └── lib.rs ├── native_api_1c_core ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml └── src │ ├── ffi │ ├── connection.rs │ ├── init_base.rs │ ├── init_done.rs │ ├── lang_extender.rs │ ├── locale_base.rs │ ├── memory_manager.rs │ ├── mod.rs │ ├── provided_types.rs │ ├── string_utils.rs │ └── user_lang_base.rs │ ├── interface.rs │ └── lib.rs ├── native_api_1c_macro ├── .github │ └── workflows │ │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── src │ ├── derive_addin │ │ ├── constants.rs │ │ ├── functions │ │ │ ├── collectors │ │ │ │ ├── call_as_func.rs │ │ │ │ ├── call_as_proc.rs │ │ │ │ ├── find_method.rs │ │ │ │ ├── get_method_name.rs │ │ │ │ ├── get_n_methods.rs │ │ │ │ ├── get_n_params.rs │ │ │ │ ├── get_param_def_value.rs │ │ │ │ ├── has_ret_val.rs │ │ │ │ └── mod.rs │ │ │ ├── generate.rs │ │ │ ├── mod.rs │ │ │ └── parse.rs │ │ ├── mod.rs │ │ ├── parsers.rs │ │ ├── props │ │ │ ├── collectors │ │ │ │ ├── find_prop.rs │ │ │ │ ├── get_n_props.rs │ │ │ │ ├── get_prop_name.rs │ │ │ │ ├── get_prop_val.rs │ │ │ │ ├── is_prop_readable.rs │ │ │ │ ├── is_prop_writable.rs │ │ │ │ ├── mod.rs │ │ │ │ └── set_prop_val.rs │ │ │ ├── generate.rs │ │ │ ├── mod.rs │ │ │ └── parse.rs │ │ └── utils.rs │ ├── extern_functions │ │ ├── mod.rs │ │ └── parse.rs │ └── lib.rs └── tests │ ├── interface │ ├── functions.rs │ └── props.rs │ └── trybuild │ ├── tests.rs │ └── to_build │ ├── functions │ ├── blob_type.rs │ ├── bool_type.rs │ ├── date_type.rs │ ├── defaults │ │ ├── blob_type.rs │ │ ├── blob_type.stderr │ │ ├── bool_type.rs │ │ ├── date_type.rs │ │ ├── date_type.stderr │ │ ├── float_type.rs │ │ ├── int_type.rs │ │ └── str_type.rs │ ├── float_type.rs │ ├── int_type.rs │ ├── out_params │ │ ├── blob_type.rs │ │ ├── bool_type.rs │ │ ├── float_type.rs │ │ ├── int_type.rs │ │ └── str_type.rs │ ├── result │ │ ├── blob_type.rs │ │ ├── bool_type.rs │ │ ├── date_type.rs │ │ ├── float_type.rs │ │ ├── int_type.rs │ │ └── str_type.rs │ └── str_type.rs │ └── props.rs └── sample_addin_rs ├── Cargo.toml └── src └── lib.rs /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build-sample-addin: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Install mingw-w64 18 | run: sudo apt-get -y install mingw-w64 19 | - name: Build sample AddIn 20 | working-directory: ./sample_addin_rs 21 | run: cargo build 22 | 23 | test-macros: 24 | 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: Install mingw-w64 30 | run: sudo apt-get -y install mingw-w64 31 | - name: Build macros 32 | working-directory: ./ 33 | run: cargo test -p native_api_1c_macro -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ./sample_addin_rs 2 | target/ 3 | Cargo.lock 4 | expand.rs 5 | lcov.info -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebekerga/native_api_1c/c6f6d1717abaa4018f16c55291cc05ebe23519ee/.gitmodules -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "native_api_1c", 4 | "native_api_1c_core", 5 | "native_api_1c_macro", 6 | "sample_addin_rs", 7 | ] 8 | 9 | [patch.crates-io] 10 | native_api_1c = { path = "native_api_1c" } 11 | native_api_1c_core = { path = "native_api_1c_core" } 12 | native_api_1c_macro = { path = "native_api_1c_macro" } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | >Гайд по использованию на русском языке можно посмотреть 2 | >[здесь](https://infostart.ru/1c/articles/1920565/) и задать вопросы по использованию, но не 3 | > оставляйте там комментарии об ошибках, т.к. там сложно это обсуждать. Лучше создайте issue в этом 4 | > репозитории. 5 | 6 | # Disclaimer 7 | 8 | This is my personal project, so there are some things coming from this fact: 9 | - While I'm trying to implement everything in an idiomatic and 'pretty' way, sometimes I just want 10 | to see progress, so some cargo clippy warnings are ignored at times, but I always try to fix them 11 | later 12 | - There'll be weeks or even months of inactivity, because I'm occupied with other things 13 | - I'll try to help anyone, who opens issue or discussion, but I can't guarantee that I'll be able 14 | to do it in a timely manner 15 | 16 | ## Contributing 17 | I'd be glad to see any contributions, but please, follow these rules: 18 | - If you want to add a feature, please, open an issue first, so we can discuss it. I don't want you 19 | to waste your time on something that I won't be accepting for one reason or another 20 | - If you want to fix a bug, better do the same, but if it's a small bug, you can just open a PR 21 | - If you want to help, but don't know what to do, you can look at issues with `help wanted` label, 22 | or just ask [in this Telegram chat](https://t.me/+2YFbh4up3y8wZmIy) 23 | 24 | # About 25 | 26 | Library for simple 1C:Enterprise platform Native API Component development, originates from findings 27 | of this [medigor/example-native-api-rs](https://github.com/medigor/example-native-api-rs) 28 | 29 | Crate is tested on Linux and Windows. It should work on MacOS as well, but it is not tested. 30 | 31 | ## A word on testing 32 | 33 | In order to test the actual FFI calls, you need to have 1C:Enterprise file base, which makes it hard 34 | to test reliably with Rust tests, *and also makes it not free :)*. One alternative is to use 35 | [OneScript](https://github.com/EvilBeaver/OneScript), which can run 1C:Enterprise scripts, including 36 | AddIn functions. However, it is not a perfect solution, because it is not 1C:Enterprise itself, and 37 | **their implementations of Native API interfaces is not the same** 38 | (See [this issue](https://github.com/EvilBeaver/OneScript/issues/1359) or try building and running 39 | [this example](https://github.com/Sebekerga/native_api_1c_go)) 40 | 41 | # Structure 42 | Library is divided into two submodules: 43 | - `native_api_1c_core` describes all necessary for implementing 1C:Enterprise Native API 44 | - `native_api_1c_macro` provides a tool for significant simplification of component implementation, 45 | taking care of `native_api_1c_core::interface::AddInWrapper` property implementation 46 | 47 | # Usage 48 | 49 | ## Attributes `#[add_in_prop(...)]` 50 | - `name` - property name in 1C 51 | - `name_ru` - property name in 1C in Russian 52 | - `readable` - property is readable from 1C 53 | - `writable` - property is writable from 1C 54 | 55 | Available property types: `i32`, `f64`, `bool`, `String` 56 | 57 | ## Functions or procedures `#[add_in_func(...)]` 58 | - `name` - property name in 1C 59 | - `name_ru` - property name in 1C in Russian 60 | ### Input arguments, `#[arg(ty = ...)]`, for each type of argument must be set, on of: 61 | | Type definition | Rust type | 1C type | 62 | |-----------------|-------------------------|-------------------------| 63 | | `Int` | `i32` | `Number` (Int) | 64 | | `Float` | `f64` | `Number` (Float or Int) | 65 | | `Bool` | `bool` | `Boolean` | 66 | | `Str` | `String` | `String` | 67 | | `Date` | `chrono::NaiveDateTime` | `Date` | 68 | | `Blob` | `Vec` | `BinaryData` | 69 | 70 | ### Return values, `#[returns(ty = ...)]`, type must be set, one of: 71 | | Type definition | Rust type | 1C type | 72 | |-----------------|-------------------------|--------------| 73 | | `Int` | `i32` | `Number` | 74 | | `Float` | `f64` | `Number` | 75 | | `Bool` | `bool` | `Boolean` | 76 | | `Str` | `String` | `String` | 77 | | `Date` | `chrono::NaiveDateTime` | `Date` | 78 | | `Blob` | `Vec` | `BinaryData` | 79 | | `None` | `()` | `Undefined` | 80 | 81 | Additionally, `Result` can be used, where `T` is one of the above. In this case, `result` 82 | must be set in `#[returns(...)]` attribute: `#[returns(Int, result)]` for `Result` 83 | 84 | ## Example 85 | 86 | ```toml 87 | # Cargo.toml 88 | [package] 89 | name = "my_addin" 90 | version = "0.1.0" 91 | edition = "2021" 92 | 93 | [lib] 94 | crate-type = ["cdylib"] 95 | 96 | [dependencies] 97 | utf16_lit = "2.0" 98 | native_api_1c = "0.10.5" 99 | ``` 100 | 101 | ```rust 102 | // src/lib.rs 103 | use std::sync::Arc; 104 | 105 | use native_api_1c::{ 106 | native_api_1c_core::ffi::connection::Connection, 107 | native_api_1c_macro::{extern_functions, AddIn}, 108 | }; 109 | 110 | #[derive(AddIn)] 111 | pub struct SampleAddIn { 112 | /// connection with 1C, used for calling events 113 | /// Arc is used to allow multiple threads to access the connection 114 | #[add_in_con] 115 | connection: Arc>, 116 | 117 | /// Property, readable and writable from 1C 118 | #[add_in_prop(ty = Int, name = "MyProp", name_ru = "МоеСвойство", readable, writable)] 119 | pub some_prop: i32, 120 | 121 | /// Property, readable from 1C but not writable 122 | #[add_in_prop(ty = Int, name = "ProtectedProp", name_ru = "ЗащищенноеСвойство", readable)] 123 | pub protected_prop: i32, 124 | 125 | /// Function, taking one or two arguments and returning a result 126 | /// In 1C it can be called as: 127 | /// ```bsl 128 | /// CompObj.MyFunction(10, 15); // 2nd arg = 15 129 | /// CompObj.MyFunction(10); // 2nd arg = 12 (default value) 130 | /// ``` 131 | /// If function returns an error, but does not panic, then 1C will throw an exception 132 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 133 | #[arg(ty = Int)] 134 | #[arg(ty = Int, default = 12)] // default value for the second argument 135 | #[returns(ty = Int, result)] 136 | pub my_function: fn(&Self, i32, i64) -> Result, 137 | 138 | /// Function, taking no arguments and returning nothing 139 | #[add_in_func(name = "MyProcedure", name_ru = "МояПроцедура")] 140 | pub my_procedure: fn(&mut Self), 141 | 142 | /// Private field, not visible from 1C 143 | private_field: i32, 144 | } 145 | 146 | impl Default for SampleAddIn { 147 | fn default() -> Self { 148 | Self { 149 | connection: Arc::new(None), 150 | some_prop: 0, 151 | protected_prop: 50, 152 | my_function: Self::my_function_inner, 153 | my_procedure: Self::my_procedure_inner, 154 | private_field: 100, 155 | } 156 | } 157 | } 158 | 159 | impl SampleAddIn { 160 | fn my_function_inner(&self, arg: i32, arg_maybe_default: i64) -> Result { 161 | Ok(self.protected_prop 162 | + self.some_prop 163 | + arg 164 | + self.private_field 165 | + arg_maybe_default as i32) 166 | } 167 | 168 | fn my_procedure_inner(&mut self) { 169 | self.protected_prop += 10; 170 | } 171 | } 172 | 173 | extern_functions! { 174 | SampleAddIn::default(), 175 | } 176 | ``` 177 | 178 | ### Adding more objects 179 | 180 | Method `extern_functions!` can take multiple objects, like this: 181 | ```rust 182 | extern_functions! { 183 | SampleAddIn::default(), 184 | AnotherAddIn::default(), 185 | YetAnotherAddIn::default(), 186 | } 187 | ``` 188 | 189 | These object must have trait `AddIn` implemented. This can be done either with `#[derive(AddIn)]` 190 | or manually. Latter is useful when you need some unusual behaviors that cannot be derived. -------------------------------------------------------------------------------- /native_api_1c/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "native_api_1c" 3 | version = "0.10.7" 4 | edition = "2021" 5 | repository = "https://github.com/Sebekerga/native_api_1c" 6 | license = "MIT" 7 | description = "Main library for Native API 1C" 8 | readme = "../README.md" 9 | 10 | [dependencies] 11 | native_api_1c_core = { path = "../native_api_1c_core" } 12 | native_api_1c_macro = { path = "../native_api_1c_macro" } 13 | 14 | [features] 15 | default = ["macro"] 16 | macro = [] 17 | -------------------------------------------------------------------------------- /native_api_1c/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 https://github.com/medigor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /native_api_1c/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub extern crate native_api_1c_core; 2 | #[cfg(feature = "macro")] 3 | pub extern crate native_api_1c_macro; 4 | -------------------------------------------------------------------------------- /native_api_1c_core/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .vscode 4 | -------------------------------------------------------------------------------- /native_api_1c_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "native_api_1c_core" 3 | version = "0.9.4" 4 | edition = "2021" 5 | repository = "https://github.com/Sebekerga/native_api_1c" 6 | license = "MIT" 7 | description = "Core library for Native API 1C" 8 | 9 | [dependencies] 10 | utf16_lit = "2.0" 11 | syn = { version = "2.0.28", features = ["full"] } 12 | quote = "1.0.32" 13 | chrono = "0.4.26" 14 | -------------------------------------------------------------------------------- /native_api_1c_core/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 https://github.com/medigor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /native_api_1c_core/README.md: -------------------------------------------------------------------------------- 1 | This is a fork of [medigor/example-native-api-rs](https://github.com/medigor/example-native-api-rs) that is made to be a core crate for 1C:Enterprise 8 Native API development. As of this moment, crate is tested on Linux and Windows. It should work on MacOS as well, but it is not tested. 2 | 3 | It implements FFI for Native API components and provides a set of types and `AddInWrapper` trait that can be used to implement 1C:Enterprise 8 Native API components in Rust. While it can be used as a standalone crate, it is intended to be used as a dependency for [native_api_1c](https://github.com/sebekerga/native_api_1c) crate. 4 | 5 | >_For FFI implementation, see [original repository](https://github.com/medigor/example-native-api-rs) or [this issue discussion](https://github.com/Sebekerga/native_api_1c/issues/2)_ 6 | 7 | Aside from some features (especially on Connection interface) not yet implemented, this crate should cover most important of the Native API functionality. -------------------------------------------------------------------------------- /native_api_1c_core/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/connection.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_long, c_ushort}; 2 | 3 | use super::{provided_types::TVariant, string_utils::os_string_nil}; 4 | 5 | /// Message codes that can be used in `Connection::add_error` method 6 | /// to specify message type. 7 | /// See [1C documentation](https://its.1c.ru/db/content/metod8dev/src/developers/platform/i8103221.htm#_com_infomessage) 8 | pub enum MessageCode { 9 | /// Error without icon 10 | None = 1000, 11 | /// Error with ">" icon 12 | Ordinary = 1001, 13 | /// Error with "!" icon 14 | Attention = 1002, 15 | /// Error with "!!" icon 16 | Important = 1003, 17 | /// Error with "!!!" icon 18 | VeryImportant = 1004, 19 | /// Error with "i" icon 20 | Info = 1005, 21 | /// Error with "err" icon 22 | Fail = 1006, 23 | /// Shows a dialog with "MB_ICONEXCLAMATION" icon 24 | DialogAttention = 1007, 25 | /// Shows a dialog with "MB_ICONINFORMATION" icon 26 | DialogInfo = 1008, 27 | /// Shows a dialog with "MB_ICONERROR" icon 28 | DialogFail = 1009, 29 | } 30 | 31 | /// VTable for Connection object, derived from Native API interface. See original 32 | /// C++ implementation in [example project](https://its.1c.ru/db/files/1CITS/EXE/VNCOMPS/VNCOMPS.zip) 33 | /// from 1C documentation 34 | #[repr(C)] 35 | struct ConnectionVTable { 36 | dtor: usize, 37 | #[cfg(target_family = "unix")] 38 | dtor2: usize, 39 | add_error: unsafe extern "system" fn( 40 | &Connection, 41 | c_ushort, 42 | *const u16, 43 | *const u16, 44 | c_long, 45 | ) -> bool, 46 | read: unsafe extern "system" fn( 47 | &Connection, 48 | *mut u16, 49 | &mut TVariant, 50 | c_long, 51 | *mut *mut u16, 52 | ) -> bool, 53 | write: 54 | unsafe extern "system" fn(&Connection, *mut u16, &mut TVariant) -> bool, 55 | register_profile_as: 56 | unsafe extern "system" fn(&Connection, *mut u16) -> bool, 57 | set_event_buffer_depth: 58 | unsafe extern "system" fn(&Connection, c_long) -> bool, 59 | get_event_buffer_depth: unsafe extern "system" fn(&Connection) -> c_long, 60 | external_event: unsafe extern "system" fn( 61 | &Connection, 62 | *mut u16, 63 | *mut u16, 64 | *mut u16, 65 | ) -> bool, 66 | clean_event_buffer: unsafe extern "system" fn(&Connection), 67 | set_status_line: unsafe extern "system" fn(&Connection, *mut u16) -> bool, 68 | reset_status_line: unsafe extern "system" fn(&Connection), 69 | } 70 | 71 | /// Connection object, used to communicate with 1C platform after the AddIn is loaded 72 | #[repr(C)] 73 | pub struct Connection { 74 | vptr1: &'static ConnectionVTable, 75 | } 76 | 77 | impl Connection { 78 | /// Equivalent to `AddError` from Native API interface and is used to add an error to the 1C platform 79 | /// # Arguments 80 | /// * `code` - message code, see [MessageCode](enum.MessageCode) 81 | /// * `source` - source of the error 82 | /// * `description` - description of the error 83 | /// # Returns 84 | /// `bool` - operation success status 85 | pub fn add_error( 86 | &self, 87 | code: MessageCode, 88 | source: &str, 89 | description: &str, 90 | ) -> bool { 91 | unsafe { 92 | let source_wstr = os_string_nil(source); 93 | let description_wstr = os_string_nil(description); 94 | (self.vptr1.add_error)( 95 | self, 96 | code as u16, 97 | source_wstr.as_ptr(), 98 | description_wstr.as_ptr(), 99 | 0, 100 | ) 101 | } 102 | } 103 | 104 | /// Equivalent to `ExternalEvent` from Native API interface and is used to send an external event to the 1C platform 105 | /// # Arguments 106 | /// * `caller` - name of the event caller 107 | /// * `name` - name of the event 108 | /// * `data` - data of the event 109 | /// # Returns 110 | /// `bool` - operation success status 111 | pub fn external_event(&self, caller: &str, name: &str, data: &str) -> bool { 112 | unsafe { 113 | let mut caller_wstr = os_string_nil(caller); 114 | let mut name_wstr = os_string_nil(name); 115 | let mut data_wstr = os_string_nil(data); 116 | (self.vptr1.external_event)( 117 | self, 118 | caller_wstr.as_mut_ptr(), 119 | name_wstr.as_mut_ptr(), 120 | data_wstr.as_mut_ptr(), 121 | ) 122 | } 123 | } 124 | 125 | /// Equivalent to `SetEventBufferDepth` from Native API interface 126 | /// # Arguments 127 | /// * `depth` - new event buffer depth 128 | pub fn set_event_buffer_depth(&self, depth: c_long) -> bool { 129 | unsafe { (self.vptr1.set_event_buffer_depth)(self, depth) } 130 | } 131 | 132 | /// Equivalent to `GetEventBufferDepth` from Native API interface 133 | /// # Returns 134 | /// `c_long` - current event buffer depth 135 | pub fn get_event_buffer_depth(&self) -> c_long { 136 | unsafe { (self.vptr1.get_event_buffer_depth)(self) } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/init_base.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_long; 2 | 3 | use super::{connection::Connection, memory_manager::MemoryManager, This}; 4 | use crate::interface::AddInWrapper; 5 | 6 | #[repr(C)] 7 | pub struct InitDoneBaseVTable { 8 | dtor: usize, 9 | #[cfg(target_family = "unix")] 10 | dtor2: usize, 11 | init: 12 | unsafe extern "system" fn(&mut This<0, T>, &'static Connection) -> bool, 13 | set_mem_manager: unsafe extern "system" fn( 14 | &mut This<0, T>, 15 | &'static MemoryManager, 16 | ) -> bool, 17 | get_info: unsafe extern "system" fn(&mut This<0, T>) -> c_long, 18 | done: unsafe extern "system" fn(&mut This<0, T>), 19 | } 20 | 21 | unsafe extern "system" fn init( 22 | this: &mut This<0, T>, 23 | interface: &'static Connection, 24 | ) -> bool { 25 | let component = this.get_component(); 26 | component.addin.init(interface) 27 | } 28 | 29 | unsafe extern "system" fn set_mem_manager( 30 | this: &mut This<0, T>, 31 | mem: &'static MemoryManager, 32 | ) -> bool { 33 | let component = this.get_component(); 34 | component.memory = Some(mem); 35 | true 36 | } 37 | 38 | unsafe extern "system" fn get_info( 39 | this: &mut This<0, T>, 40 | ) -> c_long { 41 | let component = this.get_component(); 42 | component.addin.get_info() as c_long 43 | } 44 | 45 | unsafe extern "system" fn done(this: &mut This<0, T>) { 46 | let component = this.get_component(); 47 | component.addin.done() 48 | } 49 | 50 | impl Default for InitDoneBaseVTable { 51 | fn default() -> Self { 52 | Self { 53 | dtor: 0, 54 | #[cfg(target_family = "unix")] 55 | dtor2: 0, 56 | init, 57 | set_mem_manager, 58 | get_info, 59 | done, 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/init_done.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_long; 2 | 3 | use super::{connection::Connection, memory_manager::MemoryManager, offset}; 4 | use crate::interface::AddInWrapper; 5 | 6 | type This = super::This<{ offset::INIT_DONE }, T>; 7 | 8 | #[repr(C)] 9 | pub struct InitDoneBaseVTable { 10 | dtor: usize, 11 | #[cfg(target_family = "unix")] 12 | dtor2: usize, 13 | init: unsafe extern "system" fn(&mut This, &'static Connection) -> bool, 14 | set_mem_manager: 15 | unsafe extern "system" fn(&mut This, &'static MemoryManager) -> bool, 16 | get_info: unsafe extern "system" fn(&mut This) -> c_long, 17 | done: unsafe extern "system" fn(&mut This), 18 | } 19 | 20 | unsafe extern "system" fn init( 21 | this: &mut This, 22 | interface: &'static Connection, 23 | ) -> bool { 24 | let component = this.get_component(); 25 | component.addin.init(interface) 26 | } 27 | 28 | unsafe extern "system" fn set_mem_manager( 29 | this: &mut This, 30 | mem: &'static MemoryManager, 31 | ) -> bool { 32 | let component = this.get_component(); 33 | component.memory_manager_ptr = Some(mem); 34 | true 35 | } 36 | 37 | unsafe extern "system" fn get_info( 38 | this: &mut This, 39 | ) -> c_long { 40 | let component = this.get_component(); 41 | component.addin.get_info() as c_long 42 | } 43 | 44 | unsafe extern "system" fn done(this: &mut This) { 45 | let component = this.get_component(); 46 | component.addin.done() 47 | } 48 | 49 | impl Default for InitDoneBaseVTable { 50 | fn default() -> Self { 51 | Self { 52 | dtor: 0, 53 | #[cfg(target_family = "unix")] 54 | dtor2: 0, 55 | init, 56 | set_mem_manager, 57 | get_info, 58 | done, 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/lang_extender.rs: -------------------------------------------------------------------------------- 1 | use super::{get_str, offset, provided_types::TVariant}; 2 | use crate::interface::{AddInWrapper, ParamValue, ParamValues}; 3 | use std::{ 4 | ffi::c_long, 5 | ptr::{self}, 6 | slice::from_raw_parts_mut, 7 | }; 8 | 9 | type This = super::This<{ offset::LANG_EXTENDER }, T>; 10 | 11 | #[repr(C)] 12 | pub struct LanguageExtenderBaseVTable { 13 | dtor: usize, 14 | #[cfg(target_family = "unix")] 15 | dtor2: usize, 16 | register_extension_as: 17 | unsafe extern "system" fn(&mut This, *mut *mut u16) -> bool, 18 | get_n_props: unsafe extern "system" fn(&mut This) -> c_long, 19 | find_prop: unsafe extern "system" fn(&mut This, *const u16) -> c_long, 20 | get_prop_name: 21 | unsafe extern "system" fn(&mut This, c_long, c_long) -> *const u16, 22 | get_prop_val: 23 | unsafe extern "system" fn(&mut This, c_long, &mut TVariant) -> bool, 24 | set_prop_val: 25 | unsafe extern "system" fn(&mut This, c_long, &TVariant) -> bool, 26 | is_prop_readable: unsafe extern "system" fn(&mut This, c_long) -> bool, 27 | is_prop_writable: unsafe extern "system" fn(&mut This, c_long) -> bool, 28 | get_n_methods: unsafe extern "system" fn(&mut This) -> c_long, 29 | find_method: unsafe extern "system" fn(&mut This, *const u16) -> c_long, 30 | get_method_name: 31 | unsafe extern "system" fn(&mut This, c_long, c_long) -> *const u16, 32 | get_n_params: unsafe extern "system" fn(&mut This, c_long) -> c_long, 33 | get_param_def_value: unsafe extern "system" fn( 34 | &mut This, 35 | c_long, 36 | c_long, 37 | &mut TVariant, 38 | ) -> bool, 39 | has_ret_val: unsafe extern "system" fn(&mut This, c_long) -> bool, 40 | call_as_proc: unsafe extern "system" fn( 41 | &mut This, 42 | c_long, 43 | *mut TVariant, 44 | c_long, 45 | ) -> bool, 46 | call_as_func: unsafe extern "system" fn( 47 | &mut This, 48 | c_long, 49 | &mut TVariant, 50 | *mut TVariant, 51 | c_long, 52 | ) -> bool, 53 | } 54 | 55 | unsafe extern "system" fn register_extension_as( 56 | this: &mut This, 57 | name: *mut *mut u16, 58 | ) -> bool { 59 | let component = this.get_component(); 60 | let Some(allocator) = component.memory_manager_ptr else { 61 | return false; 62 | }; 63 | 64 | let extension_name = component.addin.register_extension_as(); 65 | 66 | let Ok(ptr) = allocator.alloc_str(extension_name.len()) else { 67 | return false; 68 | }; 69 | ptr::copy_nonoverlapping( 70 | extension_name.as_ptr(), 71 | ptr.as_ptr(), 72 | extension_name.len(), 73 | ); 74 | *name = ptr.as_ptr(); 75 | 76 | true 77 | } 78 | 79 | unsafe extern "system" fn get_n_props( 80 | this: &mut This, 81 | ) -> c_long { 82 | let component = this.get_component(); 83 | component.addin.get_n_props() as c_long 84 | } 85 | 86 | unsafe extern "system" fn find_prop( 87 | this: &mut This, 88 | name: *const u16, 89 | ) -> c_long { 90 | let component = this.get_component(); 91 | let name = get_str(name); 92 | match component.addin.find_prop(name) { 93 | Some(i) => i as c_long, 94 | None => -1, 95 | } 96 | } 97 | 98 | unsafe extern "system" fn get_prop_name( 99 | this: &mut This, 100 | num: c_long, 101 | alias: c_long, 102 | ) -> *const u16 { 103 | let component = this.get_component(); 104 | let Some(allocator) = component.memory_manager_ptr else { 105 | return ptr::null(); 106 | }; 107 | let Some(prop_name) = 108 | component.addin.get_prop_name(num as usize, alias as usize) 109 | else { 110 | return ptr::null(); 111 | }; 112 | let Ok(ptr) = allocator.alloc_str(prop_name.len()) else { 113 | return ptr::null(); 114 | }; 115 | ptr::copy_nonoverlapping(prop_name.as_ptr(), ptr.as_ptr(), prop_name.len()); 116 | 117 | ptr.as_ptr() 118 | } 119 | 120 | unsafe extern "system" fn get_prop_val( 121 | this: &mut This, 122 | num: c_long, 123 | val: &mut TVariant, 124 | ) -> bool { 125 | let component = this.get_component(); 126 | let Some(mem_mngr) = component.memory_manager_ptr else { 127 | return false; 128 | }; 129 | 130 | let prop_val_result = component.addin.get_prop_val(num as usize); 131 | match prop_val_result { 132 | Ok(prop_val) => { 133 | val.update_from_return(mem_mngr, &prop_val); 134 | true 135 | } 136 | Err(_) => false, 137 | } 138 | } 139 | 140 | unsafe extern "system" fn set_prop_val( 141 | this: &mut This, 142 | num: c_long, 143 | val: &TVariant, 144 | ) -> bool { 145 | this.get_component() 146 | .addin 147 | .set_prop_val(num as usize, val.into()) 148 | .is_ok() 149 | } 150 | 151 | unsafe extern "system" fn is_prop_readable( 152 | this: &mut This, 153 | num: c_long, 154 | ) -> bool { 155 | this.get_component().addin.is_prop_readable(num as usize) 156 | } 157 | 158 | unsafe extern "system" fn is_prop_writable( 159 | this: &mut This, 160 | num: c_long, 161 | ) -> bool { 162 | let component = this.get_component(); 163 | component.addin.is_prop_writable(num as usize) 164 | } 165 | 166 | unsafe extern "system" fn get_n_methods( 167 | this: &mut This, 168 | ) -> c_long { 169 | let component = this.get_component(); 170 | component.addin.get_n_methods() as c_long 171 | } 172 | 173 | unsafe extern "system" fn find_method( 174 | this: &mut This, 175 | name: *const u16, 176 | ) -> c_long { 177 | let component = this.get_component(); 178 | let name = get_str(name); 179 | match component.addin.find_method(name) { 180 | Some(i) => i as c_long, 181 | None => -1, 182 | } 183 | } 184 | 185 | unsafe extern "system" fn get_method_name( 186 | this: &mut This, 187 | num: c_long, 188 | alias: c_long, 189 | ) -> *const u16 { 190 | let component = this.get_component(); 191 | let Some(allocator) = component.memory_manager_ptr else { 192 | return ptr::null(); 193 | }; 194 | let Some(method_name) = component 195 | .addin 196 | .get_method_name(num as usize, alias as usize) 197 | else { 198 | return ptr::null(); 199 | }; 200 | let Ok(ptr) = allocator.alloc_str(method_name.len()) else { 201 | return ptr::null(); 202 | }; 203 | 204 | ptr::copy_nonoverlapping( 205 | method_name.as_ptr(), 206 | ptr.as_ptr(), 207 | method_name.len(), 208 | ); 209 | 210 | ptr.as_ptr() 211 | } 212 | 213 | unsafe extern "system" fn get_n_params( 214 | this: &mut This, 215 | num: c_long, 216 | ) -> c_long { 217 | let component = this.get_component(); 218 | component.addin.get_n_params(num as usize) as c_long 219 | } 220 | 221 | unsafe extern "system" fn get_param_def_value( 222 | this: &mut This, 223 | method_num: c_long, 224 | param_num: c_long, 225 | val: &mut TVariant, 226 | ) -> bool { 227 | let component = this.get_component(); 228 | let Some(mem) = component.memory_manager_ptr else { 229 | return false; 230 | }; 231 | 232 | let def_value_result = component 233 | .addin 234 | .get_param_def_value(method_num as usize, param_num as usize); 235 | match def_value_result { 236 | Some(def_value) => { 237 | val.update_from_return(mem, &def_value); 238 | true 239 | } 240 | None => false, 241 | } 242 | } 243 | 244 | unsafe extern "system" fn has_ret_val( 245 | this: &mut This, 246 | method_num: c_long, 247 | ) -> bool { 248 | let component = this.get_component(); 249 | component.addin.has_ret_val(method_num as usize) 250 | } 251 | 252 | unsafe extern "system" fn call_as_proc( 253 | this: &mut This, 254 | method_num: c_long, 255 | params: *mut TVariant, 256 | size_array: c_long, 257 | ) -> bool { 258 | let component = this.get_component(); 259 | let Some(mem_mngr) = component.memory_manager_ptr else { 260 | return false; 261 | }; 262 | 263 | let parameters_raw = from_raw_parts_mut(params, size_array as usize); 264 | let mut parameters_values = 265 | ParamValues::new(parameters_raw.iter().map(ParamValue::from).collect()); 266 | 267 | let call_result = component 268 | .addin 269 | .call_as_proc(method_num as usize, &mut parameters_values); 270 | 271 | if call_result.is_err() { 272 | return false; 273 | } 274 | 275 | for (i, param) in parameters_values.iter().enumerate() { 276 | parameters_raw[i].update_from_return(mem_mngr, param); 277 | } 278 | 279 | true 280 | } 281 | 282 | unsafe extern "system" fn call_as_func( 283 | this: &mut This, 284 | method_num: c_long, 285 | ret_value: &mut TVariant, 286 | params: *mut TVariant, 287 | size_array: c_long, 288 | ) -> bool { 289 | let component = this.get_component(); 290 | let Some(mem_mngr) = component.memory_manager_ptr else { 291 | return false; 292 | }; 293 | 294 | let parameters_raw = from_raw_parts_mut(params, size_array as usize); 295 | let mut parameters_values = 296 | ParamValues::new(parameters_raw.iter().map(ParamValue::from).collect()); 297 | 298 | let call_result = component 299 | .addin 300 | .call_as_func(method_num as usize, &mut parameters_values); 301 | 302 | let Ok(ret_val) = call_result else { 303 | return false; 304 | }; 305 | 306 | ret_value.update_from_return(mem_mngr, &ret_val); 307 | 308 | for (i, param) in parameters_values.iter().enumerate() { 309 | parameters_raw[i].update_from_return(mem_mngr, param); 310 | } 311 | 312 | true 313 | } 314 | 315 | impl Default for LanguageExtenderBaseVTable { 316 | fn default() -> Self { 317 | Self { 318 | dtor: 0, 319 | #[cfg(target_family = "unix")] 320 | dtor2: 0, 321 | register_extension_as, 322 | get_n_props, 323 | find_prop, 324 | get_prop_name, 325 | get_prop_val, 326 | set_prop_val, 327 | is_prop_readable, 328 | is_prop_writable, 329 | get_n_methods, 330 | find_method, 331 | get_method_name, 332 | get_n_params, 333 | get_param_def_value, 334 | has_ret_val, 335 | call_as_proc, 336 | call_as_func, 337 | } 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/locale_base.rs: -------------------------------------------------------------------------------- 1 | use crate::interface::AddInWrapper; 2 | 3 | use super::string_utils::get_str; 4 | 5 | type This = super::This<{ super::offset::LOCALE }, T>; 6 | 7 | #[repr(C)] 8 | pub struct LocaleBaseVTable { 9 | dtor: usize, 10 | #[cfg(target_family = "unix")] 11 | dtor2: usize, 12 | set_locale: unsafe extern "system" fn(&mut This, *const u16), 13 | } 14 | 15 | impl Default for LocaleBaseVTable { 16 | fn default() -> Self { 17 | Self { 18 | dtor: 0, 19 | #[cfg(target_family = "unix")] 20 | dtor2: 0, 21 | set_locale, 22 | } 23 | } 24 | } 25 | 26 | unsafe extern "system" fn set_locale( 27 | this: &mut This, 28 | loc: *const u16, 29 | ) { 30 | let component = this.get_component(); 31 | let loc = get_str(loc); 32 | component.addin.set_locale(loc) 33 | } 34 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/memory_manager.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::{c_ulong, c_void}, 3 | ptr::{self, NonNull}, 4 | }; 5 | 6 | /// VTable for MemoryManager object, derived from Native API interface. See original 7 | /// C++ implementation in [example project](https://its.1c.ru/db/files/1CITS/EXE/VNCOMPS/VNCOMPS.zip) 8 | /// from 1C documentation 9 | #[repr(C)] 10 | struct MemoryManagerVTable { 11 | dtor: usize, 12 | #[cfg(target_family = "unix")] 13 | dtor2: usize, 14 | alloc_memory: unsafe extern "system" fn( 15 | &MemoryManager, 16 | *mut *mut c_void, 17 | c_ulong, 18 | ) -> bool, 19 | free_memory: unsafe extern "system" fn(&MemoryManager, *mut *mut c_void), 20 | } 21 | 22 | /// MemoryManager object, used to allocate memory for the AddIn 23 | #[repr(C)] 24 | pub struct MemoryManager { 25 | vptr: &'static MemoryManagerVTable, 26 | } 27 | 28 | pub struct AllocationError; 29 | 30 | impl MemoryManager { 31 | /// Safe wrapper around `alloc_memory` method of the MemoryManager object 32 | /// to allocate memory for byte array 33 | /// # Arguments 34 | /// * `size` - size of the memory block to allocate 35 | /// # Returns 36 | /// `Result, AllocationError>` - pointer to the allocated memory block 37 | pub fn alloc_blob( 38 | &self, 39 | size: usize, 40 | ) -> Result, AllocationError> { 41 | let mut ptr = ptr::null_mut::(); 42 | unsafe { 43 | if (self.vptr.alloc_memory)(self, &mut ptr, size as c_ulong * 2) { 44 | match NonNull::new(ptr as *mut u8) { 45 | Some(ptr) => Ok(ptr), 46 | None => Err(AllocationError), 47 | } 48 | } else { 49 | Err(AllocationError) 50 | } 51 | } 52 | } 53 | 54 | /// Safe wrapper around `alloc_memory` method of the MemoryManager object 55 | /// to allocate memory for UTF-16 string 56 | /// # Arguments 57 | /// * `size` - size of the memory block to allocate 58 | /// # Returns 59 | /// `Result, AllocationError>` - pointer to the allocated memory block 60 | pub fn alloc_str( 61 | &self, 62 | size: usize, 63 | ) -> Result, AllocationError> { 64 | let mut ptr = ptr::null_mut::(); 65 | unsafe { 66 | if (self.vptr.alloc_memory)(self, &mut ptr, size as c_ulong * 2) { 67 | match NonNull::new(ptr as *mut u16) { 68 | Some(ptr) => Ok(ptr), 69 | None => Err(AllocationError), 70 | } 71 | } else { 72 | Err(AllocationError) 73 | } 74 | } 75 | } 76 | 77 | pub fn free_memory(&self, ptr: &mut *mut c_void) { 78 | unsafe { 79 | (self.vptr.free_memory)(self, ptr); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! FFI bindings for 1C:Enterprise Native API are divided between several 3 | //! submodules according to what C++ class they originate from 4 | //! 5 | use std::{ 6 | ffi::{c_long, c_void}, 7 | ptr, 8 | }; 9 | 10 | use crate::interface::AddInWrapper; 11 | 12 | use self::{ 13 | connection::Connection, init_done::InitDoneBaseVTable, 14 | lang_extender::LanguageExtenderBaseVTable, locale_base::LocaleBaseVTable, 15 | memory_manager::MemoryManager, string_utils::get_str, 16 | user_lang_base::UserLanguageBaseVTable, 17 | }; 18 | 19 | /// Implementation of `Connection` - replacement for `IAddInDefBase` 20 | pub mod connection; 21 | /// Implementation of `InitDone` - replacement for `IInitDoneBase` 22 | pub mod init_done; 23 | /// Implementation of `LanguageExtender` - replacement for `ILanguageExtenderBase` 24 | pub mod lang_extender; 25 | /// Implementation of `LocaleBase` 26 | pub mod locale_base; 27 | /// Implementation of `MemoryManager` - replacement for `IMemoryManager` 28 | pub mod memory_manager; 29 | /// Implementations of types, provided by Native API for easy of use in Rust 30 | pub mod provided_types; 31 | /// Functions to convert between Rust and 1C strings 32 | pub mod string_utils; 33 | /// Implementation of `UserLanguageBase` 34 | pub mod user_lang_base; 35 | 36 | /// Scheme of attaching to 1C platform process 37 | #[repr(C)] 38 | #[derive(Debug)] 39 | #[allow(dead_code)] 40 | pub enum AttachType { 41 | /// Attach to 1C platform process 42 | NotIsolated = 1, 43 | /// Attach to separate process 44 | Isolated, 45 | /// Any of the above 46 | Any, 47 | } 48 | 49 | /// Struct to extract pointer to `Component` from it's interface components 50 | /// In some places we need to get pointer to `Component` from it's interface 51 | /// components, so we need to calculate offset of `Component` in memory 52 | #[repr(C)] 53 | struct This { 54 | ptr: *mut Component, 55 | } 56 | 57 | mod offset { 58 | pub const INIT_DONE: usize = 0; 59 | pub const LANG_EXTENDER: usize = 1; 60 | pub const LOCALE: usize = 2; 61 | pub const USER_LANG: usize = 3; 62 | } 63 | 64 | impl<'a, const OFFSET: usize, T: AddInWrapper> This { 65 | unsafe fn get_component(&mut self) -> &'a mut Component { 66 | let new_ptr = (self as *mut This as *mut c_void) 67 | .sub(OFFSET * std::mem::size_of::()); 68 | &mut *(new_ptr as *mut Component) 69 | } 70 | } 71 | 72 | #[repr(C)] 73 | struct Component { 74 | // 1C Interface 75 | init_done_ptr: Box>, 76 | lang_extender_ptr: Box>, 77 | locale_ptr: Box>, 78 | usr_lang_ptr: Box>, 79 | 80 | // storage for additional interfaces 81 | memory_manager_ptr: Option<&'static MemoryManager>, 82 | connection_ptr: Option<&'static Connection>, 83 | locale: Option, 84 | user_interface_language_code: Option, 85 | 86 | // rust part 87 | destroy: unsafe extern "system" fn(*mut *mut Component), 88 | addin: T, 89 | } 90 | 91 | unsafe extern "system" fn destroy( 92 | component: *mut *mut Component, 93 | ) { 94 | let comp = Box::from_raw(*component); 95 | drop(comp); 96 | } 97 | 98 | /// # Safety 99 | /// `component` must be a valid pointer to a `Component` from GetClassObject call 100 | /// `addin` must be a valid `AddInWrapper` instance 101 | pub unsafe fn create_component( 102 | component: *mut *mut c_void, 103 | addin: T, 104 | ) -> c_long { 105 | let c = Box::new(Component { 106 | init_done_ptr: Default::default(), 107 | lang_extender_ptr: Default::default(), 108 | locale_ptr: Default::default(), 109 | usr_lang_ptr: Default::default(), 110 | 111 | destroy: destroy::, 112 | memory_manager_ptr: Default::default(), 113 | connection_ptr: Default::default(), 114 | locale: Default::default(), 115 | user_interface_language_code: Default::default(), 116 | addin, 117 | }); 118 | 119 | *component = Box::into_raw(c) as *mut c_void; 120 | 1 121 | } 122 | 123 | /// # Safety 124 | /// `component` must be a valid pointer to a `Component` from DestroyObject call 125 | pub unsafe fn destroy_component(component: *mut *mut c_void) -> c_long { 126 | #[repr(C)] 127 | struct ComponentWrapper { 128 | vptr1: usize, 129 | vptr2: usize, 130 | vptr3: usize, 131 | vptr4: usize, 132 | destroy: unsafe extern "system" fn(*mut *mut c_void), 133 | } 134 | 135 | let wrapper = *component as *mut ComponentWrapper; 136 | let wrapper = &mut *wrapper; 137 | (wrapper.destroy)(component); 138 | *component = ptr::null_mut(); 139 | 140 | 0 141 | } 142 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/provided_types.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::{c_int, c_void}, 3 | ptr, 4 | slice::from_raw_parts, 5 | }; 6 | 7 | use chrono::{Datelike, Timelike}; 8 | 9 | use crate::interface::ParamValue; 10 | 11 | use super::memory_manager::{AllocationError, MemoryManager}; 12 | 13 | /// Type representing 1C date and time values 14 | /// # Fields 15 | /// * `sec` - seconds after the minute - [0, 60] including leap second 16 | /// * `min` - minutes after the hour - [0, 59] 17 | /// * `hour` - hours since midnight - [0, 23] 18 | /// * `mday` - day of the month - [1, 31] 19 | /// * `mon` - month of the year - [0, 11] 20 | /// * `year` - years since 1900 21 | /// * `wday` - days since Sunday - [0, 6] 22 | /// * `yday` - days since January 1 - [0, 365] 23 | /// * `isdst` - daylight savings time flag 24 | /// * `gmtoff` - seconds east of UTC (unix only) 25 | /// * `zone` - timezone abbreviation (unix only) 26 | #[repr(C)] 27 | #[derive(Clone, Copy, Default, Debug)] 28 | pub struct Tm { 29 | pub sec: c_int, 30 | pub min: c_int, 31 | pub hour: c_int, 32 | pub mday: c_int, 33 | pub mon: c_int, 34 | pub year: c_int, 35 | pub wday: c_int, 36 | pub yday: c_int, 37 | pub isdst: c_int, 38 | 39 | #[cfg(target_family = "unix")] 40 | pub gmtoff: std::ffi::c_long, 41 | #[cfg(target_family = "unix")] 42 | pub zone: std::ffi::c_char, 43 | } 44 | 45 | impl From<&Tm> for chrono::NaiveDateTime { 46 | fn from(value: &Tm) -> Self { 47 | let default_time = chrono::NaiveDate::from_ymd_opt(1970, 1, 1) 48 | .unwrap() 49 | .and_hms_opt(0, 0, 0) 50 | .unwrap(); 51 | 52 | let from_result = chrono::NaiveDate::from_ymd_opt( 53 | 1900 + value.year, 54 | (1 + value.mon) as u32, 55 | (value.mday) as u32, 56 | ) 57 | .ok_or(()); 58 | if from_result.is_err() { 59 | return default_time; 60 | } 61 | 62 | let from_result = from_result.unwrap().and_hms_opt( 63 | value.hour as u32, 64 | value.min as u32, 65 | value.sec as u32, 66 | ); 67 | if from_result.is_none() { 68 | return default_time; 69 | } 70 | 71 | from_result.unwrap() 72 | } 73 | } 74 | 75 | impl From for chrono::NaiveDateTime { 76 | fn from(value: Tm) -> Self { 77 | Self::from(&value) 78 | } 79 | } 80 | 81 | impl From<&chrono::NaiveDateTime> for Tm { 82 | fn from(value: &chrono::NaiveDateTime) -> Self { 83 | Self { 84 | sec: value.time().second() as c_int, 85 | min: value.time().minute() as c_int, 86 | hour: value.time().hour() as c_int, 87 | mday: value.date().day() as c_int, 88 | mon: value.date().month0() as c_int, 89 | year: value.date().year() as c_int - 1900, 90 | wday: value.date().weekday().num_days_from_sunday() as c_int, 91 | yday: value.ordinal0() as c_int, 92 | isdst: 0, 93 | #[cfg(target_family = "unix")] 94 | gmtoff: 0, 95 | #[cfg(target_family = "unix")] 96 | zone: 0, 97 | } 98 | } 99 | } 100 | 101 | impl From for Tm { 102 | fn from(value: chrono::NaiveDateTime) -> Self { 103 | Self::from(&value) 104 | } 105 | } 106 | 107 | #[cfg(target_family = "unix")] 108 | impl PartialEq for Tm { 109 | fn eq(&self, other: &Self) -> bool { 110 | self.sec == other.sec 111 | && self.min == other.min 112 | && self.hour == other.hour 113 | && self.mday == other.mday 114 | && self.mon == other.mon 115 | && self.year == other.year 116 | && self.wday == other.wday 117 | && self.yday == other.yday 118 | && self.isdst == other.isdst 119 | && self.gmtoff == other.gmtoff 120 | && self.zone == other.zone 121 | } 122 | } 123 | 124 | #[cfg(target_family = "windows")] 125 | impl PartialEq for Tm { 126 | fn eq(&self, other: &Self) -> bool { 127 | self.sec == other.sec 128 | && self.min == other.min 129 | && self.hour == other.hour 130 | && self.mday == other.mday 131 | && self.mon == other.mon 132 | && self.year == other.year 133 | && self.wday == other.wday 134 | && self.yday == other.yday 135 | && self.isdst == other.isdst 136 | } 137 | } 138 | 139 | /// Type representing 1C variant values 140 | /// # Fields 141 | /// `mem` - pointer to the MemoryManager object 142 | /// `variant` - pointer to the TVariant object 143 | /// `result` - pointer to the result of the operation 144 | pub struct ReturnValue<'a> { 145 | pub mem: &'a MemoryManager, 146 | pub variant: &'a mut TVariant, 147 | pub result: &'a mut bool, 148 | } 149 | 150 | #[allow(dead_code)] 151 | impl<'a> ReturnValue<'a> { 152 | /// Creates a new ReturnValue object 153 | pub fn new( 154 | mem: &'a MemoryManager, 155 | variant: &'a mut TVariant, 156 | result: &'a mut bool, 157 | ) -> Self { 158 | Self { 159 | mem, 160 | variant, 161 | result, 162 | } 163 | } 164 | 165 | /// Sets the value of the ReturnValue object to empty 166 | pub fn set_empty(self) { 167 | self.variant.vt = VariantType::Empty; 168 | } 169 | 170 | /// Sets the value of the ReturnValue object to integer `i32` 171 | pub fn set_i32(self, val: i32) { 172 | self.variant.vt = VariantType::Int32; 173 | self.variant.value.i32 = val; 174 | } 175 | 176 | /// Sets the value of the ReturnValue object to bool `bool` 177 | pub fn set_bool(self, val: bool) { 178 | self.variant.vt = VariantType::Bool; 179 | self.variant.value.bool = val; 180 | } 181 | 182 | /// Sets the value of the ReturnValue object to float `f64` 183 | pub fn set_f64(self, val: f64) { 184 | self.variant.vt = VariantType::Double; 185 | self.variant.value.f64 = val; 186 | } 187 | 188 | /// Sets the value of the ReturnValue object to date-time `Tm` 189 | pub fn set_date(self, val: Tm) { 190 | self.variant.vt = VariantType::Time; 191 | self.variant.value.tm = val; 192 | } 193 | 194 | /// Sets the value of the ReturnValue object to UTF-16 `&[u16]` 195 | pub fn set_str(self, val: &[u16]) { 196 | let Ok(ptr) = self.mem.alloc_str(val.len()) else { 197 | *self.result = false; 198 | return; 199 | }; 200 | 201 | unsafe { 202 | ptr::copy_nonoverlapping(val.as_ptr(), ptr.as_ptr(), val.len()) 203 | }; 204 | 205 | self.variant.vt = VariantType::WStr; 206 | self.variant.value.data_str.ptr = ptr.as_ptr(); 207 | self.variant.value.data_str.len = val.len() as u32; 208 | } 209 | 210 | /// Sets the value of the ReturnValue object to blob `&[u8]` 211 | pub fn set_blob(self, val: &[u8]) { 212 | let Ok(ptr) = self.mem.alloc_blob(val.len()) else { 213 | *self.result = false; 214 | return; 215 | }; 216 | 217 | unsafe { 218 | ptr::copy_nonoverlapping(val.as_ptr(), ptr.as_ptr(), val.len()) 219 | }; 220 | 221 | self.variant.vt = VariantType::Blob; 222 | self.variant.value.data_blob.ptr = ptr.as_ptr(); 223 | self.variant.value.data_blob.len = val.len() as u32; 224 | } 225 | } 226 | 227 | impl<'a> From<&'a TVariant> for ParamValue { 228 | fn from(param: &'a TVariant) -> ParamValue { 229 | unsafe { 230 | match param.vt { 231 | VariantType::Empty => Self::Empty, 232 | VariantType::Bool => Self::Bool(param.value.bool), 233 | VariantType::Int32 => Self::I32(param.value.i32), 234 | VariantType::Double => Self::F64(param.value.f64), 235 | VariantType::Time => Self::Date(param.value.tm), 236 | VariantType::WStr => Self::String( 237 | from_raw_parts( 238 | param.value.data_str.ptr, 239 | param.value.data_str.len as usize, 240 | ) 241 | .into(), 242 | ), 243 | VariantType::Blob => Self::Blob( 244 | from_raw_parts( 245 | param.value.data_blob.ptr, 246 | param.value.data_blob.len as usize, 247 | ) 248 | .into(), 249 | ), 250 | _ => Self::Empty, 251 | } 252 | } 253 | } 254 | } 255 | 256 | #[repr(u16)] 257 | #[allow(dead_code)] 258 | #[derive(PartialEq, Debug)] 259 | pub enum VariantType { 260 | Empty = 0, 261 | Null, 262 | Int16, //int16_t 263 | Int32, //int32_t 264 | Float, //float 265 | Double, //double 266 | Date, //DATE (double) 267 | Time, //struct tm 268 | PStr, //struct str string 269 | Interface, //struct iface 270 | Error, //int32_t errCode 271 | Bool, //bool 272 | Variant, //struct _tVariant * 273 | Int8, //int8_t 274 | UInt8, //uint8_t 275 | UInt16, //uint16_t 276 | UInt32, //uint32_t 277 | Int64, //int64_t 278 | UInt64, //uint64_t 279 | Int, //int Depends on architecture 280 | UInt, //unsigned int Depends on architecture 281 | HResult, //long hRes 282 | WStr, //struct wstr 283 | Blob, //means in struct str binary data contain 284 | ClsID, //UUID 285 | 286 | Undefined = 0xFFFF, 287 | } 288 | 289 | /// Type representing stored String data 290 | /// # Fields 291 | /// * `ptr` - pointer to the data 292 | /// * `len` - length of the data 293 | #[repr(C)] 294 | #[derive(Clone, Copy)] 295 | pub struct DataStr { 296 | pub ptr: *mut u16, 297 | pub len: u32, 298 | } 299 | 300 | /// Type representing stored Blob data 301 | /// # Fields 302 | /// * `ptr` - pointer to the data 303 | /// * `len` - length of the data 304 | #[repr(C)] 305 | #[derive(Clone, Copy)] 306 | pub struct DataBlob { 307 | pub ptr: *mut u8, 308 | pub len: u32, 309 | } 310 | 311 | /// Type encapsulating 1C variant values 312 | /// # Fields 313 | /// * `bool` - boolean value 314 | /// * `i32` - integer value 315 | /// * `f64` - float value 316 | /// * `tm` - date-time value 317 | /// * `data_str` - UTF-16 string value 318 | /// * `data_blob` - blob value 319 | #[repr(C)] 320 | pub union VariantValue { 321 | pub bool: bool, 322 | pub i32: i32, 323 | pub f64: f64, 324 | pub tm: Tm, 325 | pub data_str: DataStr, 326 | pub data_blob: DataBlob, 327 | } 328 | 329 | /// Type encapsulating 1C variant values for internal use 330 | #[repr(C)] 331 | pub struct TVariant { 332 | pub value: VariantValue, 333 | pub elements: u32, //Dimension for an one-dimensional array in pvarVal 334 | pub vt: VariantType, 335 | } 336 | 337 | impl Default for TVariant { 338 | fn default() -> Self { 339 | Self { 340 | value: VariantValue { bool: false }, 341 | elements: 0, 342 | vt: VariantType::Empty, 343 | } 344 | } 345 | } 346 | 347 | impl TVariant { 348 | /// # Safety 349 | /// This function is unsafe because it manipulates pointers, provided by the 1C platform. 350 | /// Function is safe as long as 1C platform provides valid pointers. 351 | pub unsafe fn update_to_str( 352 | &mut self, 353 | mem_mngr: &MemoryManager, 354 | v: &[u16], 355 | ) -> Result { 356 | let old_pointer = self.value.data_str.ptr; 357 | 358 | let ptr = mem_mngr.alloc_str(v.len())?; 359 | ptr::copy_nonoverlapping(v.as_ptr(), ptr.as_ptr(), v.len()); 360 | 361 | self.value.data_str.ptr = ptr.as_ptr(); 362 | self.value.data_str.len = v.len() as u32; 363 | 364 | mem_mngr.free_memory(&mut old_pointer.cast::()); 365 | 366 | self.vt = VariantType::WStr; 367 | 368 | Ok(self.value.data_str.len) 369 | } 370 | 371 | /// # Safety 372 | /// This function is unsafe because it manipulates pointers, provided by the 1C platform. 373 | /// Function is safe as long as 1C platform provides valid pointers. 374 | pub unsafe fn update_to_blob( 375 | &mut self, 376 | mem_mngr: &MemoryManager, 377 | v: &[u8], 378 | ) -> Result { 379 | let old_pointer = self.value.data_blob.ptr; 380 | 381 | let ptr = mem_mngr.alloc_blob(v.len())?; 382 | ptr::copy_nonoverlapping(v.as_ptr(), ptr.as_ptr(), v.len()); 383 | 384 | self.value.data_blob.ptr = ptr.as_ptr(); 385 | self.value.data_blob.len = v.len() as u32; 386 | 387 | mem_mngr.free_memory(&mut old_pointer.cast::()); 388 | 389 | self.vt = VariantType::Blob; 390 | 391 | Ok(self.value.data_blob.len) 392 | } 393 | 394 | pub fn update_to_bool(&mut self, v: bool) { 395 | self.value.bool = v; 396 | self.vt = VariantType::Bool; 397 | } 398 | 399 | pub fn update_to_i32(&mut self, v: i32) { 400 | self.value.i32 = v; 401 | self.vt = VariantType::Int32; 402 | } 403 | 404 | pub fn update_to_f64(&mut self, v: f64) { 405 | self.value.f64 = v; 406 | self.vt = VariantType::Double; 407 | } 408 | 409 | pub fn update_to_date(&mut self, v: Tm) { 410 | self.value.tm = v; 411 | self.vt = VariantType::Time; 412 | } 413 | 414 | pub fn update_from_return( 415 | &mut self, 416 | mem_mngr: &MemoryManager, 417 | value: &ParamValue, 418 | ) { 419 | match value { 420 | ParamValue::Empty => self.vt = VariantType::Empty, 421 | ParamValue::Bool(v) => self.update_to_bool(*v), 422 | ParamValue::I32(v) => self.update_to_i32(*v), 423 | ParamValue::F64(v) => self.update_to_f64(*v), 424 | ParamValue::Date(v) => self.update_to_date(*v), 425 | ParamValue::String(v) => { 426 | let _ = unsafe { self.update_to_str(mem_mngr, v.as_slice()) }; 427 | } 428 | ParamValue::Blob(v) => { 429 | let _ = unsafe { self.update_to_blob(mem_mngr, v.as_slice()) }; 430 | } 431 | } 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/string_utils.rs: -------------------------------------------------------------------------------- 1 | use std::slice::from_raw_parts; 2 | 3 | /// Helper function to convert pointer to UTF-16 string to Rust slice 4 | /// # Arguments 5 | /// * `s` - pointer to UTF-16 string 6 | /// # Returns 7 | /// `&[u16]` - slice of UTF-16 characters 8 | /// # Safety 9 | /// This function is unsafe because it takes a raw pointer and dereferences it 10 | pub unsafe fn get_str<'a>(s: *const u16) -> &'a [u16] { 11 | unsafe fn strlen(s: *const u16) -> usize { 12 | let mut i = 0; 13 | while *s.add(i) != 0 { 14 | i += 1; 15 | } 16 | i + 1 17 | } 18 | 19 | let len = strlen(s); 20 | from_raw_parts(s, len) 21 | } 22 | 23 | /// Helper function to convert Rust string to UTF-16 string 24 | /// # Arguments 25 | /// * `s` - Rust string 26 | /// # Returns 27 | /// `Vec` - UTF-16 string without null terminator 28 | #[cfg(target_family = "unix")] 29 | pub fn os_string_nil(s: &str) -> Vec { 30 | s.encode_utf16().collect() 31 | } 32 | 33 | /// Helper function to convert Rust string to UTF-16 string 34 | /// # Arguments 35 | /// * `s` - Rust string 36 | /// # Returns 37 | /// `Vec` - UTF-16 string with null terminator 38 | #[cfg(target_family = "windows")] 39 | pub fn os_string_nil(s: &str) -> Vec { 40 | let os_str = std::ffi::OsStr::new(s); 41 | std::os::windows::prelude::OsStrExt::encode_wide(os_str) 42 | .chain(Some(0).into_iter()) 43 | .collect() 44 | } 45 | 46 | /// Helper function to convert Rust string to UTF-16 string 47 | /// # Arguments 48 | /// * `s` - Rust string 49 | /// # Returns 50 | /// `Vec` - UTF-16 string without null terminator 51 | #[cfg(target_family = "unix")] 52 | pub fn os_string(s: &str) -> Vec { 53 | s.encode_utf16().collect() 54 | } 55 | 56 | /// Helper function to convert Rust string to UTF-16 string 57 | /// # Arguments 58 | /// * `s` - Rust string 59 | /// # Returns 60 | /// `Vec` - UTF-16 string with null terminator 61 | #[cfg(target_family = "windows")] 62 | pub fn os_string(s: &str) -> Vec { 63 | let os_str = std::ffi::OsStr::new(s); 64 | std::os::windows::prelude::OsStrExt::encode_wide(os_str).collect() 65 | } 66 | 67 | /// Helper function to convert UTF-16 string to Rust string 68 | /// # Arguments 69 | /// * `s` - UTF-16 string 70 | /// # Returns 71 | /// `String` - Rust string 72 | pub fn from_os_string(s: &[u16]) -> String { 73 | String::from_utf16_lossy(s) 74 | .trim_end_matches(char::from(0)) 75 | .to_string() 76 | } 77 | -------------------------------------------------------------------------------- /native_api_1c_core/src/ffi/user_lang_base.rs: -------------------------------------------------------------------------------- 1 | use crate::interface::AddInWrapper; 2 | 3 | use super::string_utils::get_str; 4 | 5 | type This = super::This<{ super::offset::USER_LANG }, T>; 6 | 7 | #[repr(C)] 8 | pub struct UserLanguageBaseVTable { 9 | dtor: usize, 10 | #[cfg(target_family = "unix")] 11 | dtor2: usize, 12 | set_user_interface_language_code: 13 | unsafe extern "system" fn(&mut This, *const u16), 14 | } 15 | 16 | impl Default for UserLanguageBaseVTable { 17 | fn default() -> Self { 18 | Self { 19 | dtor: 0, 20 | #[cfg(target_family = "unix")] 21 | dtor2: 0, 22 | set_user_interface_language_code, 23 | } 24 | } 25 | } 26 | 27 | unsafe extern "system" fn set_user_interface_language_code( 28 | this: &mut This, 29 | lang: *const u16, 30 | ) { 31 | let component = this.get_component(); 32 | let lang = get_str(lang); 33 | component.addin.set_user_interface_language_code(lang) 34 | } 35 | -------------------------------------------------------------------------------- /native_api_1c_core/src/interface.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Index, IndexMut}; 2 | 3 | use crate::ffi::{connection::Connection, provided_types::Tm}; 4 | 5 | /// Represents 1C variant values for parameters in safe Rust code. 6 | #[derive(Clone, Debug)] 7 | pub enum ParamValue { 8 | /// Empty value 9 | Empty, 10 | /// Boolean value 11 | Bool(bool), 12 | /// Integer value 13 | I32(i32), 14 | /// Float value 15 | F64(f64), 16 | /// Date-time value 17 | Date(Tm), 18 | /// UTF-16 string value 19 | String(Vec), 20 | /// Blob value 21 | Blob(Vec), 22 | } 23 | 24 | impl ParamValue { 25 | pub fn set_bool(&mut self, val: bool) { 26 | *self = Self::Bool(val); 27 | } 28 | 29 | pub fn set_i32(&mut self, val: i32) { 30 | *self = Self::I32(val); 31 | } 32 | 33 | pub fn set_f64(&mut self, val: f64) { 34 | *self = Self::F64(val); 35 | } 36 | 37 | pub fn set_date(&mut self, val: Tm) { 38 | *self = Self::Date(val); 39 | } 40 | 41 | pub fn set_str(&mut self, val: Vec) { 42 | *self = Self::String(val); 43 | } 44 | 45 | pub fn set_blob(&mut self, val: Vec) { 46 | *self = Self::Blob(val); 47 | } 48 | } 49 | 50 | impl PartialEq for ParamValue { 51 | fn eq(&self, other: &Self) -> bool { 52 | match (self, other) { 53 | (Self::Empty, Self::Empty) => true, 54 | (Self::Bool(a), Self::Bool(b)) => a == b, 55 | (Self::I32(a), Self::I32(b)) => a == b, 56 | (Self::F64(a), Self::F64(b)) => a == b, 57 | (Self::Date(a), Self::Date(b)) => a == b, 58 | (Self::String(a), Self::String(b)) => a == b, 59 | (Self::Blob(a), Self::Blob(b)) => a == b, 60 | _ => false, 61 | } 62 | } 63 | } 64 | 65 | /// Represents 1C variant values for return values in safe Rust code. 66 | /// Only creator of the object can set the initial value, therefor has 67 | /// control over count of values. 68 | #[derive(Clone)] 69 | pub struct ParamValues { 70 | values: Vec, 71 | } 72 | 73 | impl ParamValues { 74 | pub fn new(values: Vec) -> Self { 75 | Self { values } 76 | } 77 | 78 | pub fn len(&self) -> usize { 79 | self.values.len() 80 | } 81 | 82 | pub fn is_empty(&self) -> bool { 83 | self.values.is_empty() 84 | } 85 | 86 | pub fn iter(&self) -> std::slice::Iter { 87 | self.values.iter() 88 | } 89 | } 90 | 91 | impl Index for ParamValues { 92 | type Output = ParamValue; 93 | 94 | fn index(&self, index: usize) -> &Self::Output { 95 | &self.values[index] 96 | } 97 | } 98 | 99 | impl IndexMut for ParamValues { 100 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 101 | &mut self.values[index] 102 | } 103 | } 104 | 105 | pub type AddInWrapperResult = Result; 106 | 107 | /// `AddInWrapper` trait is used to implement the 1C AddIn interface, 108 | /// and is used in FFI to get necessary information about the AddIn 109 | /// and call its methods. 110 | /// 111 | /// All trait methods, that return bool, should return true if the operation was successful 112 | /// and false otherwise. 113 | /// 114 | /// Many of them are equivalents of methods in the 1C AddIn interface, and their 115 | /// descriptions can be found in the [1C documentation](https://its.1c.ru/db/metod8dev/content/3221/hdoc). 116 | #[allow(clippy::result_unit_err)] 117 | pub trait AddInWrapper { 118 | /// Equivalent to `Init` from Native API interface and is called when the AddIn is loaded by 1C platform 119 | /// and is used to pass the pointer to the 1C Connection object 120 | /// # Arguments 121 | /// * `interface` - pointer to the 1C Connection object 122 | /// # Returns 123 | /// `bool` - operation success status 124 | fn init(&mut self, interface: &'static Connection) -> bool; 125 | 126 | /// Equivalent to `GetInfo` from Native API interface and is used to get Native API version used by AddIn, either 127 | /// `1000` meaning 1.0 or `2000` meaning 2.0. It will be later removed to only 128 | /// support 2.0 version. 129 | /// # Returns 130 | /// `u16` - Native API version 131 | fn get_info(&self) -> u16 { 132 | 2000 133 | } 134 | 135 | /// Equivalent to `Done` from Native API interface and is called when the AddIn is unloaded by 1C platform 136 | fn done(&mut self); 137 | 138 | /// Equivalent to `RegisterExtensionAs` from Native API interface and is used to get the name of the AddIn 139 | /// as it will be shown in 1C platform 140 | /// # Returns 141 | /// `&[u16]` - name of the AddIn in UTF-16 with null-terminator 142 | fn register_extension_as(&mut self) -> &[u16]; 143 | 144 | /// Equivalent to `GetNProps` from Native API interface and is used to get the number of properties 145 | /// that the AddIn has that can be accessed by 1C platform 146 | /// # Returns 147 | /// `usize` - number of properties 148 | fn get_n_props(&self) -> usize; 149 | 150 | /// Equivalent to `FindProp` from Native API interface and is used to get the index of the property 151 | /// with the given name 152 | /// # Arguments 153 | /// * `name` - name of the property in UTF-16 154 | /// # Returns 155 | /// `Option` - index of the property or None if the property was not found 156 | fn find_prop(&self, name: &[u16]) -> Option; 157 | 158 | /// Equivalent to `GetPropName` from Native API interface and is used to get the name of the property 159 | /// with the given index 160 | /// # Arguments 161 | /// * `num` - index of the property 162 | /// * `alias` - alias of the property, usually 0 for Russian and 1 for English 163 | /// # Returns 164 | /// `Option>` - name of the property in UTF-16 or None if the property was not found 165 | fn get_prop_name(&self, num: usize, alias: usize) -> Option>; 166 | 167 | /// Equivalent to `GetPropVal` from Native API interface and is used to get the value of the property 168 | /// with the given index 169 | /// # Arguments 170 | /// * `num` - index of the property 171 | /// # Returns 172 | /// `AddInWrapperResult` - value of the property, or error if the property was not found 173 | fn get_prop_val(&self, num: usize) -> AddInWrapperResult; 174 | 175 | /// Equivalent to `SetPropVal` from Native API interface and is used to set the value of the property 176 | /// with the given index 177 | /// # Arguments 178 | /// * `num` - index of the property 179 | /// * `val` - value of the property 180 | /// # Returns 181 | /// `AddInWrapperResult<()>` - operation result 182 | fn set_prop_val( 183 | &mut self, 184 | num: usize, 185 | val: ParamValue, 186 | ) -> AddInWrapperResult<()>; 187 | 188 | /// Equivalent to `IsPropReadable` from Native API interface and is used to check if the property 189 | /// with the given index is readable 190 | /// # Arguments 191 | /// * `num` - index of the property 192 | /// # Returns 193 | /// `bool` - if the property is readable 194 | fn is_prop_readable(&self, num: usize) -> bool; 195 | 196 | /// Equivalent to `IsPropWritable` from Native API interface and is used to check if the property 197 | /// with the given index is writable 198 | /// # Arguments 199 | /// * `num` - index of the property 200 | /// # Returns 201 | /// `bool` - if the property is writable 202 | fn is_prop_writable(&self, num: usize) -> bool; 203 | 204 | /// Equivalent to `GetNMethods` from Native API interface and is used to get the number of methods 205 | /// that the AddIn has that can be called by 1C platform 206 | /// # Returns 207 | /// `usize` - number of methods 208 | fn get_n_methods(&self) -> usize; 209 | 210 | /// Equivalent to `FindMethod` from Native API interface and is used to get the index of method 211 | /// with the given name 212 | /// # Arguments 213 | /// * `name` - name of method in UTF-16 214 | /// # Returns 215 | /// `Option` - index of method or None if method was not found 216 | fn find_method(&self, name: &[u16]) -> Option; 217 | 218 | /// Equivalent to `GetMethodName` from Native API interface and is used to get the name of method 219 | /// with the given index 220 | /// # Arguments 221 | /// * `num` - index of method 222 | /// * `alias` - alias of method, usually 0 for Russian and 1 for English 223 | /// # Returns 224 | /// `Option>` - name of method in UTF-16 or None if method was not found 225 | fn get_method_name(&self, num: usize, alias: usize) -> Option>; 226 | 227 | /// Equivalent to `GetNParams` from Native API interface and is used to get the number of parameters 228 | /// that method with the given index has 229 | /// # Arguments 230 | /// * `num` - index of method 231 | /// # Returns 232 | /// `usize` - number of parameters 233 | fn get_n_params(&self, num: usize) -> usize; 234 | 235 | /// Equivalent to `GetParamDefValue` from Native API interface and is used to get the default value 236 | /// of the parameter 237 | /// # Arguments 238 | /// * `method_num` - index of method 239 | /// * `param_num` - index of parameter 240 | /// # Returns 241 | /// `Option` - default value of the parameter or None if the parameter was not found 242 | fn get_param_def_value( 243 | &self, 244 | method_num: usize, 245 | param_num: usize, 246 | ) -> Option; 247 | 248 | /// Equivalent to `HasRetVal` from Native API interface and is used to check if method 249 | /// with the given index returns a value 250 | /// # Arguments 251 | /// * `method_num` - index of method 252 | /// # Returns 253 | /// `bool` - if method returns a value 254 | fn has_ret_val(&self, method_num: usize) -> bool; 255 | 256 | /// Equivalent to `CallAsProc` from Native API interface and is used to call method 257 | /// with the given index as a procedure, meaning that it does not return a value 258 | /// # Arguments 259 | /// * `method_num` - index of method 260 | /// * `params` - slice of ParamValue objects that contain the parameters 261 | /// # Returns 262 | /// `AddInWrapperResult<()>` - operation result 263 | fn call_as_proc( 264 | &mut self, 265 | method_num: usize, 266 | params: &mut ParamValues, 267 | ) -> AddInWrapperResult<()>; 268 | 269 | /// Equivalent to `CallAsFunc` from Native API interface and is used to call method 270 | /// with the given index as a function, meaning that it returns a value 271 | /// # Arguments 272 | /// * `method_num` - index of method 273 | /// * `params` - slice of ParamValue objects that contain the parameters 274 | /// # Returns 275 | /// `AddInWrapperResult` - result of the method 276 | fn call_as_func( 277 | &mut self, 278 | method_num: usize, 279 | params: &mut ParamValues, 280 | ) -> AddInWrapperResult; 281 | 282 | /// Equivalent to `SetLocale` from Native API interface and is used to set the locale 283 | /// of the AddIn. It's marked as deprecated in 1C documentation, but is still available 284 | /// for use with platform versions prior to 8.3.21 285 | fn set_locale(&mut self, loc: &[u16]); 286 | 287 | /// Equivalent to `SetUserInterfaceLanguageCode` from Native API interface and is used to 288 | /// pass the language code of the 1C platform interface to the AddIn 289 | /// # Arguments 290 | /// * `lang` - language code in UTF-16, two letters 291 | fn set_user_interface_language_code(&mut self, lang: &[u16]); 292 | } 293 | -------------------------------------------------------------------------------- /native_api_1c_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Crate for working with 1C:Enterprise Native API. It contains 2 | //! low level FFI, moved from original C++ implementation, and high level 3 | //! interface for working with Native API from RUST. It attempts to be as 4 | //! close to original C++ implementation as possible, but some changes are 5 | //! made to make it more idiomatic in RUST. 6 | //! 7 | //! While it is possible to use this crate to implement your Native API 8 | //! Component, it is intended to be used with native_api_1c crate. 9 | 10 | /// Module for implementations of Native API FFI 11 | pub mod ffi; 12 | /// Module for high level interface of Native API 13 | pub mod interface; 14 | -------------------------------------------------------------------------------- /native_api_1c_macro/.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Install mingw-w64 20 | run: sudo apt-get -y install mingw-w64 21 | - name: Install target 22 | run: rustup target add x86_64-pc-windows-gnu 23 | - name: Build 24 | run: cargo build --target=x86_64-pc-windows-gnu --verbose 25 | - name: Run tests 26 | run: cargo test --verbose 27 | -------------------------------------------------------------------------------- /native_api_1c_macro/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /native_api_1c_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "native_api_1c_macro" 3 | version = "0.10.5" 4 | edition = "2021" 5 | repository = "https://github.com/Sebekerga/native_api_1c" 6 | license = "MIT" 7 | description = "Macro library for Native API 1C" 8 | 9 | [[test]] 10 | name = "trybuild" 11 | path = "tests/trybuild/tests.rs" 12 | 13 | [[test]] 14 | name = "props_interface" 15 | path = "tests/interface/props.rs" 16 | 17 | [[test]] 18 | name = "functions_interface" 19 | path = "tests/interface/functions.rs" 20 | 21 | [lib] 22 | proc-macro = true 23 | 24 | [dependencies] 25 | syn = { version = "2.0.38", features = ["default", "printing"] } 26 | quote = "1.0.32" 27 | proc-macro2 = "1.0.66" 28 | darling = "0.20.3" 29 | 30 | [dev-dependencies] 31 | native_api_1c = { path = "../native_api_1c" } 32 | trybuild = { version = "1.0.49", features = ["diff"] } 33 | utf16_lit = "2.0" 34 | chrono = "0.4.26" 35 | rstest = "0.21.0" 36 | -------------------------------------------------------------------------------- /native_api_1c_macro/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 https://github.com/medigor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /native_api_1c_macro/README.md: -------------------------------------------------------------------------------- 1 | ```rust 2 | use std::sync::Arc; 3 | 4 | use native_api_1c::{ 5 | native_api_1c_core::ffi::connection::Connection, 6 | native_api_1c_macro::{extern_functions, AddIn}, 7 | }; 8 | 9 | #[derive(AddIn)] 10 | pub struct MyAddIn { 11 | /// connection with 1C, used for calling events 12 | /// Arc is used to allow multiple threads to access the connection 13 | #[add_in_con] 14 | connection: Arc>, 15 | 16 | /// Property, readable and writable from 1C 17 | #[add_in_prop(ty = Int, name = "MyProp", name_ru = "МоеСвойство", readable, writable)] 18 | pub some_prop: i32, 19 | 20 | /// Property, readable from 1C but not writable 21 | #[add_in_prop(ty = Int, name = "ProtectedProp", name_ru = "ЗащищенноеСвойство", readable)] 22 | pub protected_prop: i32, 23 | 24 | /// Function, taking one or two arguments and returning a result 25 | /// In 1C it can be called as: 26 | /// ```bsl 27 | /// ComponentObject.MyFunction(10, 15); // 2nd argument = 15 28 | /// ComponentObject.MyFunction(10); // 2nd argument = 12 (default value) 29 | /// ``` 30 | /// If function returns an error, but does not panic, then 1C will throw an exception 31 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 32 | #[arg(ty = Int)] 33 | #[arg(ty = Int, default = 12)] 34 | #[returns(ty = Int, result)] 35 | pub my_function: fn(&Self, i32, i64) -> Result, 36 | 37 | /// Function, taking no arguments and returning nothing 38 | #[add_in_func(name = "MyProcedure", name_ru = "МояПроцедура")] 39 | #[returns(ty = Str)] 40 | pub my_procedure: fn(&mut Self) -> String, 41 | 42 | /// Private field, not visible from 1C 43 | private_field: i32, 44 | } 45 | 46 | impl Default for MyAddIn { 47 | fn default() -> Self { 48 | Self { 49 | connection: Arc::new(None), 50 | some_prop: 0, 51 | protected_prop: 50, 52 | my_function: Self::my_function_inner, 53 | my_procedure: Self::my_procedure_inner, 54 | private_field: 100, 55 | } 56 | } 57 | } 58 | 59 | impl MyAddIn { 60 | fn my_function_inner(&self, arg: i32, arg_maybe_default: i64) -> Result { 61 | Ok(self.protected_prop 62 | + self.some_prop 63 | + arg 64 | + self.private_field 65 | + arg_maybe_default as i32) 66 | } 67 | 68 | fn my_procedure_inner(&mut self) -> String { 69 | self.protected_prop += 1; 70 | "Some string from rust".to_string() 71 | } 72 | } 73 | 74 | extern_functions! { 75 | MyAddIn::default(), 76 | } 77 | ``` -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/constants.rs: -------------------------------------------------------------------------------- 1 | pub const BOOL_TYPE: &str = "Bool"; 2 | pub const I32_TYPE: &str = "Int"; 3 | pub const F64_TYPE: &str = "Float"; 4 | pub const STRING_TYPE: &str = "Str"; 5 | pub const DATE_TYPE: &str = "Date"; 6 | pub const BLOB_TYPE: &str = "Blob"; 7 | pub const UNTYPED_TYPE: &str = "None"; 8 | 9 | pub const ALL_RETURN_TYPES: &[&str] = &[ 10 | BOOL_TYPE, 11 | I32_TYPE, 12 | F64_TYPE, 13 | STRING_TYPE, 14 | DATE_TYPE, 15 | BLOB_TYPE, 16 | UNTYPED_TYPE, 17 | ]; 18 | pub const ALL_ARG_TYPES: &[&str] = &[ 19 | BOOL_TYPE, 20 | I32_TYPE, 21 | F64_TYPE, 22 | STRING_TYPE, 23 | DATE_TYPE, 24 | BLOB_TYPE, 25 | ]; 26 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/call_as_func.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::Ident; 4 | 5 | use crate::derive_addin::functions::{generate::func_call_tkn, FuncDesc}; 6 | 7 | use super::{empty_func_collector_error, FunctionCollector}; 8 | 9 | pub struct CallAsFuncCollector { 10 | generated: Result, 11 | } 12 | 13 | impl Default for CallAsFuncCollector { 14 | fn default() -> Self { 15 | Self { 16 | generated: Err(empty_func_collector_error()), 17 | } 18 | } 19 | } 20 | 21 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for CallAsFuncCollector { 22 | fn from_iter>(iter: T) -> Self { 23 | let mut body = TokenStream::new(); 24 | for (func_index, func_desc) in iter { 25 | if func_desc.return_value.ty.is_none() { 26 | // Skip functions without return value 27 | continue; 28 | } 29 | 30 | let return_val_ident = Ident::new("val", proc_macro2::Span::call_site()); 31 | let call_func = func_call_tkn(func_desc, Some(&return_val_ident)); 32 | body.extend(quote! { 33 | if method_num == #func_index { 34 | #call_func 35 | return Ok(val); 36 | }; 37 | }); 38 | } 39 | 40 | let definition = quote! { 41 | fn call_as_func( 42 | &mut self, 43 | method_num: usize, 44 | params: &mut native_api_1c::native_api_1c_core::interface::ParamValues, 45 | ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult< 46 | native_api_1c::native_api_1c_core::interface::ParamValue 47 | > { 48 | #body 49 | Err(()) 50 | } 51 | }; 52 | 53 | Self { 54 | generated: Ok(definition), 55 | } 56 | } 57 | } 58 | 59 | impl FunctionCollector<'_> for CallAsFuncCollector { 60 | fn release(self) -> Result { 61 | self.generated 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/call_as_proc.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::functions::{generate::func_call_tkn, FuncDesc}; 5 | 6 | use super::{empty_func_collector_error, FunctionCollector}; 7 | 8 | pub struct CallAsProcCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for CallAsProcCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_func_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for CallAsProcCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut body = TokenStream::new(); 23 | for (func_index, func_desc) in iter { 24 | let call_func = func_call_tkn(func_desc, None); 25 | body.extend(quote! { 26 | if method_num == #func_index { 27 | #call_func 28 | return Ok(()); 29 | }; 30 | }); 31 | } 32 | 33 | let definition = quote! { 34 | fn call_as_proc( 35 | &mut self, 36 | method_num: usize, 37 | params: &mut native_api_1c::native_api_1c_core::interface::ParamValues, 38 | ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult<()> { 39 | #body 40 | Err(()) 41 | } 42 | }; 43 | 44 | Self { 45 | generated: Ok(definition), 46 | } 47 | } 48 | } 49 | 50 | impl FunctionCollector<'_> for CallAsProcCollector { 51 | fn release(self) -> Result { 52 | self.generated 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/find_method.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::functions::FuncDesc; 5 | 6 | use super::{empty_func_collector_error, FunctionCollector}; 7 | 8 | pub struct FindMethodCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for FindMethodCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_func_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for FindMethodCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut find_method_body = TokenStream::new(); 23 | 24 | for (func_index, func_desc) in iter { 25 | let name_literal = func_desc.name_literal.clone(); 26 | let name_ru_literal = func_desc.name_ru_literal.clone(); 27 | 28 | find_method_body.extend(quote! { 29 | if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil(#name_literal) == name { 30 | return Some(#func_index) 31 | }; 32 | if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil(#name_ru_literal) == name { 33 | return Some(#func_index) 34 | }; 35 | }); 36 | } 37 | 38 | let find_method_definition = quote! { 39 | fn find_method(&self, name: &[u16]) -> Option { 40 | #find_method_body 41 | None 42 | } 43 | }; 44 | 45 | Self { 46 | generated: Ok(find_method_definition), 47 | } 48 | } 49 | } 50 | 51 | impl FunctionCollector<'_> for FindMethodCollector { 52 | fn release(self) -> Result { 53 | self.generated 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/get_method_name.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::functions::FuncDesc; 5 | 6 | use super::{empty_func_collector_error, FunctionCollector}; 7 | 8 | pub struct GetMethodNameCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for GetMethodNameCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_func_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for GetMethodNameCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut get_func_name_body = TokenStream::new(); 23 | 24 | for (func_index, func_desc) in iter { 25 | let name_literal = func_desc.name_literal.clone(); 26 | let name_ru_literal = func_desc.name_ru_literal.clone(); 27 | 28 | get_func_name_body.extend(quote! { 29 | #get_func_name_body 30 | if num == #func_index && alias == 0 { 31 | return Some(native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( 32 | #name_literal).into() 33 | ) 34 | }; 35 | if num == #func_index { 36 | return Some(native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil( 37 | #name_ru_literal).into() 38 | ) 39 | }; 40 | }); 41 | } 42 | 43 | let get_func_name_definition = quote! { 44 | fn get_method_name(&self, num: usize, alias: usize) -> Option> { 45 | #get_func_name_body 46 | None 47 | } 48 | }; 49 | 50 | Self { 51 | generated: Ok(get_func_name_definition), 52 | } 53 | } 54 | } 55 | 56 | impl FunctionCollector<'_> for GetMethodNameCollector { 57 | fn release(self) -> Result { 58 | self.generated 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/get_n_methods.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::functions::FuncDesc; 5 | 6 | use super::{empty_func_collector_error, FunctionCollector}; 7 | 8 | pub struct GetNMethodsCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for GetNMethodsCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_func_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for GetNMethodsCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let number_of_func = iter.into_iter().count(); 23 | 24 | let find_method_definition = quote! { 25 | fn get_n_methods(&self) -> usize { 26 | #number_of_func 27 | } 28 | }; 29 | 30 | Self { 31 | generated: Ok(find_method_definition), 32 | } 33 | } 34 | } 35 | 36 | impl FunctionCollector<'_> for GetNMethodsCollector { 37 | fn release(self) -> Result { 38 | self.generated 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/get_n_params.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::functions::FuncDesc; 5 | 6 | use super::{empty_func_collector_error, FunctionCollector}; 7 | 8 | pub struct GetNParamsCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for GetNParamsCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_func_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for GetNParamsCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut body = TokenStream::new(); 23 | 24 | for (func_index, func_desc) in iter { 25 | let number_of_params = func_desc.get_1c_params().len(); 26 | body.extend(quote! { 27 | if num == #func_index { return #number_of_params }; 28 | }); 29 | } 30 | 31 | let definition = quote! { 32 | fn get_n_params(&self, num: usize) -> usize { 33 | #body 34 | 0 35 | } 36 | }; 37 | 38 | Self { 39 | generated: Ok(definition), 40 | } 41 | } 42 | } 43 | 44 | impl FunctionCollector<'_> for GetNParamsCollector { 45 | fn release(self) -> Result { 46 | self.generated 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/get_param_def_value.rs: -------------------------------------------------------------------------------- 1 | use super::super::super::utils::expr_to_os_value; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | 5 | use crate::derive_addin::functions::{FuncDesc, FuncParamType}; 6 | 7 | use super::{empty_func_collector_error, FunctionCollector}; 8 | 9 | pub struct GetParamDefValueCollector { 10 | generated: Result, 11 | } 12 | 13 | impl Default for GetParamDefValueCollector { 14 | fn default() -> Self { 15 | Self { 16 | generated: Err(empty_func_collector_error()), 17 | } 18 | } 19 | } 20 | 21 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for GetParamDefValueCollector { 22 | fn from_iter>(iter: T) -> Self { 23 | let mut body = TokenStream::new(); 24 | 25 | for (func_index, func_desc) in iter { 26 | let mut func_body = quote! {}; 27 | for (arg_index, arg_desc) in func_desc.get_1c_params().iter().enumerate() { 28 | let Some(expr) = &arg_desc.default else { 29 | // Skip parameters without default value 30 | continue; 31 | }; 32 | 33 | let FuncParamType::PlatformType(ty) = &arg_desc.ty else { 34 | // Skip parameters that is not platform type 35 | continue; 36 | }; 37 | 38 | let expr = expr_to_os_value(expr, ty, true); 39 | func_body.extend(quote! { 40 | if param_num == #arg_index { 41 | return Some(#expr); 42 | } 43 | }) 44 | } 45 | body.extend(quote! { 46 | if method_num == #func_index { 47 | #func_body 48 | return None; 49 | }; 50 | }); 51 | } 52 | 53 | let definition = quote! { 54 | fn get_param_def_value( 55 | &self, 56 | method_num: usize, 57 | param_num: usize, 58 | ) -> Option { 59 | #body 60 | None 61 | } 62 | }; 63 | 64 | Self { 65 | generated: Ok(definition), 66 | } 67 | } 68 | } 69 | 70 | impl FunctionCollector<'_> for GetParamDefValueCollector { 71 | fn release(self) -> Result { 72 | self.generated 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/has_ret_val.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::functions::FuncDesc; 5 | 6 | use super::{empty_func_collector_error, FunctionCollector}; 7 | 8 | pub struct HasReturnValueCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for HasReturnValueCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_func_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a FuncDesc)> for HasReturnValueCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut body = TokenStream::new(); 23 | 24 | for (func_index, func_desc) in iter { 25 | let has_ret_val = func_desc.return_value.ty.is_some(); 26 | body.extend(quote! { 27 | if method_num == #func_index { return #has_ret_val }; 28 | }); 29 | } 30 | 31 | let definition = quote! { 32 | fn has_ret_val(&self, method_num: usize) -> bool { 33 | #body 34 | false 35 | } 36 | }; 37 | 38 | Self { 39 | generated: Ok(definition), 40 | } 41 | } 42 | } 43 | 44 | impl FunctionCollector<'_> for HasReturnValueCollector { 45 | fn release(self) -> Result { 46 | self.generated 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/collectors/mod.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | 3 | use super::FuncDesc; 4 | use crate::derive_addin::utils::macros::tkn_err_inner; 5 | 6 | pub mod call_as_func; 7 | pub mod call_as_proc; 8 | pub mod find_method; 9 | pub mod get_method_name; 10 | pub mod get_n_methods; 11 | pub mod get_n_params; 12 | pub mod get_param_def_value; 13 | pub mod has_ret_val; 14 | 15 | pub use call_as_func::CallAsFuncCollector; 16 | pub use call_as_proc::CallAsProcCollector; 17 | pub use find_method::FindMethodCollector; 18 | pub use get_method_name::GetMethodNameCollector; 19 | pub use get_n_methods::GetNMethodsCollector; 20 | pub use get_n_params::GetNParamsCollector; 21 | pub use get_param_def_value::GetParamDefValueCollector; 22 | pub use has_ret_val::HasReturnValueCollector; 23 | 24 | pub trait FunctionCollector<'a>: FromIterator<(usize, &'a FuncDesc)> + Default { 25 | fn release(self) -> Result; 26 | } 27 | 28 | pub fn empty_func_collector_error() -> darling::Error { 29 | tkn_err_inner!("No functions found", &proc_macro2::Span::call_site()) 30 | } 31 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/generate.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::{quote, ToTokens}; 3 | use syn::Ident; 4 | 5 | use crate::derive_addin::utils::{expr_from_os_value, expr_to_os_value}; 6 | 7 | use super::{FuncArgumentDesc, FuncDesc, FuncParamType}; 8 | 9 | pub fn func_call_tkn(func: &FuncDesc, set_to: Option<&Ident>) -> TokenStream { 10 | let func_ident = func.ident.clone(); 11 | 12 | let mut pre_call = quote! {}; 13 | let mut func_args = quote! {}; 14 | let mut post_call = quote! {}; 15 | 16 | for (param_index, param_desc) in func.get_1c_params().iter().enumerate() { 17 | let param_ident = Ident::new(&format!("param_{}", param_index + 1), Span::call_site()); 18 | 19 | let (pre_call_param, post_call_param) = 20 | gen_param_prep(param_desc, param_index, ¶m_ident); 21 | 22 | if func_args.is_empty() { 23 | func_args.extend(quote! {#param_ident}) 24 | } else { 25 | func_args.extend(quote! {, #param_ident}); 26 | } 27 | 28 | pre_call.extend(pre_call_param); 29 | post_call.extend(post_call_param); 30 | } 31 | 32 | if func.has_self_param() { 33 | if func_args.is_empty() { 34 | func_args = quote! {self}; 35 | } else { 36 | func_args = quote! {self, #func_args}; 37 | } 38 | } 39 | 40 | let mut func_call = quote! { 41 | let call_result = (self.#func_ident)(#func_args); 42 | }; 43 | 44 | if func.return_value.result { 45 | func_call.extend(quote! { 46 | if call_result.is_err() { 47 | return Err(()); 48 | } 49 | let call_result = call_result.unwrap(); 50 | }); 51 | }; 52 | 53 | if let Some(set_to) = set_to { 54 | let return_ty = func.return_value.ty.clone().unwrap(); 55 | let result_wrap = expr_to_os_value("e! { call_result }, &return_ty, true); 56 | func_call.extend(quote! { 57 | let #set_to 58 | }); 59 | func_call.extend(quote! { = }); 60 | func_call.extend(quote! {#result_wrap;}); 61 | } 62 | 63 | quote! { 64 | #pre_call 65 | #func_call 66 | #post_call 67 | } 68 | } 69 | 70 | fn gen_param_prep( 71 | param: &FuncArgumentDesc, 72 | param_index: usize, 73 | param_ident: &Ident, 74 | ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { 75 | let FuncParamType::PlatformType(param_ty) = ¶m.ty else { 76 | panic!("SelfType is not allowed here"); 77 | }; 78 | 79 | let param_unwrap = expr_from_os_value("e! { params[#param_index]}, param_ty); 80 | let mut pre_call = quote! {; 81 | let #param_ident = #param_unwrap; 82 | let mut #param_ident = #param_ident.clone().into(); 83 | }; 84 | if param.out_param { 85 | pre_call.extend(quote! { 86 | let #param_ident = &mut #param_ident; 87 | }); 88 | } 89 | 90 | let post_call = if !param.out_param { 91 | quote! {} 92 | } else { 93 | let param_wrap = expr_to_os_value(¶m_ident.to_token_stream(), param_ty, false); 94 | let mut q = quote! { 95 | params[#param_index] 96 | }; 97 | q.extend(quote! { = }); 98 | q.extend(quote! { #param_wrap; }); 99 | q 100 | }; 101 | 102 | (pre_call, post_call) 103 | } 104 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use darling::FromMeta; 4 | use proc_macro2::{Ident, TokenStream}; 5 | use quote::{quote, ToTokens}; 6 | 7 | use super::{ 8 | constants::{BLOB_TYPE, BOOL_TYPE, DATE_TYPE, F64_TYPE, I32_TYPE, STRING_TYPE}, 9 | parsers::ParamType, 10 | }; 11 | 12 | pub mod collectors; 13 | pub mod generate; 14 | pub mod parse; 15 | 16 | #[derive(Debug)] 17 | pub struct FuncDesc { 18 | pub ident: Ident, 19 | 20 | pub name_literal: TokenStream, 21 | pub name_ru_literal: TokenStream, 22 | 23 | pub params: Vec, 24 | pub return_value: ReturnTypeDesc, 25 | } 26 | 27 | impl FuncDesc { 28 | pub fn get_1c_params(&self) -> Vec<&FuncArgumentDesc> { 29 | self.params 30 | .iter() 31 | .filter(|param| !matches!(param.ty, FuncParamType::SelfType)) 32 | .collect() 33 | } 34 | 35 | pub fn has_self_param(&self) -> bool { 36 | self.params 37 | .iter() 38 | .any(|param| matches!(param.ty, FuncParamType::SelfType)) 39 | } 40 | } 41 | 42 | #[derive(Debug)] 43 | pub struct FuncArgumentDesc { 44 | pub ty: FuncParamType, 45 | pub default: Option, 46 | pub out_param: bool, 47 | } 48 | 49 | #[derive(Debug)] 50 | pub struct ReturnTypeDesc { 51 | pub ty: Option, 52 | pub result: bool, 53 | } 54 | const META_TYPE_ERR: &str = "expected string literal or path"; 55 | 56 | #[derive(Clone, Debug, PartialEq)] 57 | pub enum FuncParamType { 58 | SelfType, 59 | PlatformType(ParamType), 60 | } 61 | 62 | impl Display for FuncParamType { 63 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 64 | let type_str = match self { 65 | FuncParamType::SelfType => "Self".to_string(), 66 | FuncParamType::PlatformType(param_type) => format!("{param_type:?}"), 67 | }; 68 | write!(f, "{}", type_str) 69 | } 70 | } 71 | 72 | impl FromMeta for FuncParamType { 73 | fn from_expr(expr: &syn::Expr) -> darling::Result { 74 | let meta_type_err = darling::Error::custom(META_TYPE_ERR); 75 | let expr_string = match expr { 76 | syn::Expr::Lit(str_lit) => match str_lit.lit { 77 | syn::Lit::Str(ref str) => str.value(), 78 | _ => return Err(meta_type_err), 79 | }, 80 | syn::Expr::Path(path) => path.path.segments.first().unwrap().ident.to_string(), 81 | _ => return Err(meta_type_err), 82 | }; 83 | Self::from_string(&expr_string) 84 | } 85 | 86 | fn from_string(value: &str) -> darling::Result { 87 | let joined_allowed_types = crate::derive_addin::constants::ALL_ARG_TYPES.join(", "); 88 | Self::try_from(value).map_err(|_| { 89 | darling::Error::custom(format!( 90 | "unknown type `{value}`. Must be one of: {joined_allowed_types}", 91 | )) 92 | }) 93 | } 94 | } 95 | 96 | impl TryFrom<&str> for FuncParamType { 97 | type Error = (); 98 | 99 | fn try_from(value: &str) -> Result { 100 | match value { 101 | BOOL_TYPE => Ok(FuncParamType::PlatformType(ParamType::Bool)), 102 | I32_TYPE => Ok(FuncParamType::PlatformType(ParamType::I32)), 103 | F64_TYPE => Ok(FuncParamType::PlatformType(ParamType::F64)), 104 | STRING_TYPE => Ok(FuncParamType::PlatformType(ParamType::String)), 105 | DATE_TYPE => Ok(FuncParamType::PlatformType(ParamType::Date)), 106 | BLOB_TYPE => Ok(FuncParamType::PlatformType(ParamType::Blob)), 107 | _ => Err(()), 108 | } 109 | } 110 | } 111 | 112 | impl ToTokens for FuncParamType { 113 | fn to_tokens(&self, tokens: &mut TokenStream) { 114 | *tokens = match self { 115 | FuncParamType::SelfType => panic!("type not supported for selection"), 116 | FuncParamType::PlatformType(param_type) => match param_type { 117 | ParamType::Bool => { 118 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Bool } 119 | } 120 | ParamType::I32 => { 121 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::I32 } 122 | } 123 | ParamType::F64 => { 124 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::F64 } 125 | } 126 | ParamType::String => { 127 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::String } 128 | } 129 | ParamType::Date => { 130 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Date } 131 | } 132 | ParamType::Blob => { 133 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Blob } 134 | } 135 | }, 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/functions/parse.rs: -------------------------------------------------------------------------------- 1 | use darling::{FromField, FromMeta}; 2 | use proc_macro2::{Span, TokenStream}; 3 | use quote::ToTokens; 4 | use syn::{spanned::Spanned, Attribute, DataStruct, Meta}; 5 | 6 | use crate::derive_addin::{ 7 | parsers::{ParamType, PropName}, 8 | utils::ident_option_to_darling_err, 9 | }; 10 | 11 | use super::{FuncArgumentDesc, FuncDesc, FuncParamType, ReturnTypeDesc}; 12 | 13 | impl FromField for FuncDesc { 14 | fn from_field(field: &syn::Field) -> darling::Result { 15 | let field_ident = ident_option_to_darling_err(field.ident.as_ref())?; 16 | 17 | let add_in_func_attr: Vec<&Attribute> = field 18 | .attrs 19 | .iter() 20 | .filter(|attr| attr.path().is_ident("add_in_func")) 21 | .collect(); 22 | if add_in_func_attr.is_empty() { 23 | return Err( 24 | darling::Error::custom("Field must have `add_in_func` attribute") 25 | .with_span(field_ident), 26 | ); 27 | } else if add_in_func_attr.len() > 1 { 28 | return Err( 29 | darling::Error::custom("Field can have only 1 `add_in_func` attribute") 30 | .with_span(field_ident), 31 | ); 32 | }; 33 | let add_in_func_attr = add_in_func_attr[0]; 34 | 35 | let arg_attrs: Vec<&Attribute> = field 36 | .attrs 37 | .iter() 38 | .filter(|attr| attr.path().is_ident("arg")) 39 | .collect(); 40 | 41 | let returns_attrs: Vec<&Attribute> = field 42 | .attrs 43 | .iter() 44 | .filter(|attr| attr.path().is_ident("returns")) 45 | .collect(); 46 | if returns_attrs.len() > 1 { 47 | return Err( 48 | darling::Error::custom("Field can have at most 1 `returns` attribute") 49 | .with_span(field_ident), 50 | ); 51 | }; 52 | let returns_attr = returns_attrs.first().copied(); 53 | 54 | let func_meta = FuncHeadMeta::from_meta(&add_in_func_attr.meta)?; 55 | let params_meta = arg_attrs 56 | .iter() 57 | .map(|attr| FuncArgumentMeta::from_meta(&attr.meta)) 58 | .collect::>>()?; 59 | let return_meta = returns_attr 60 | .map(|attr| FuncReturnMeta::from_meta(&attr.meta)) 61 | .transpose()?; 62 | let return_value = match return_meta { 63 | Some(meta) => ReturnTypeDesc::from(meta), 64 | None => ReturnTypeDesc { 65 | ty: None, 66 | result: false, 67 | }, 68 | }; 69 | 70 | let mut params = params_meta 71 | .into_iter() 72 | .map(FuncArgumentDesc::try_from) 73 | .map(|res| res.map_err(|err| err.into())) 74 | .collect::, darling::Error>>()?; 75 | 76 | let syn::Type::BareFn(bare_fn) = &field.ty else { 77 | return Err( 78 | darling::Error::custom("AddIn functions must have bare `fn` type") 79 | .with_span(field_ident), 80 | ); 81 | }; 82 | 83 | if let Some(first_input) = bare_fn.inputs.first() { 84 | let arg_tkn_stream: TokenStream = first_input.to_token_stream(); 85 | 86 | if let Ok(reference) = syn::parse2::(arg_tkn_stream.clone()) { 87 | if arg_tkn_stream 88 | .into_iter() 89 | .filter(|t| t.to_string() == "Self") 90 | .count() 91 | == 1 92 | { 93 | params.insert( 94 | 0, 95 | FuncArgumentDesc { 96 | ty: FuncParamType::SelfType, 97 | default: None, 98 | out_param: reference.mutability.is_some(), 99 | }, 100 | ) 101 | }; 102 | }; 103 | }; 104 | 105 | Ok(Self { 106 | ident: field_ident.to_owned(), 107 | 108 | name_literal: func_meta.name.into(), 109 | name_ru_literal: func_meta.name_ru.into(), 110 | 111 | params, 112 | return_value, 113 | }) 114 | } 115 | } 116 | 117 | #[derive(FromMeta, Debug)] 118 | struct FuncHeadMeta { 119 | name: PropName, 120 | name_ru: PropName, 121 | } 122 | 123 | #[derive(FromMeta, Debug)] 124 | struct FuncArgumentMeta { 125 | ident: Option, 126 | ty: FuncParamType, 127 | default: Option, 128 | #[allow(dead_code)] 129 | as_in: Option<()>, 130 | as_out: Option<()>, 131 | } 132 | 133 | impl TryFrom for FuncArgumentDesc { 134 | type Error = ErrorConvertingMeta; 135 | 136 | fn try_from(arg_meta: FuncArgumentMeta) -> Result { 137 | if arg_meta.as_in.is_some() && arg_meta.as_out.is_some() { 138 | return Err(Self::Error::ConflictingParams( 139 | arg_meta.ident.span(), 140 | "as_in".to_string(), 141 | "as_out".to_string(), 142 | )); 143 | } 144 | 145 | let allowed_defaults = match arg_meta.ty.clone() { 146 | FuncParamType::SelfType => false, 147 | FuncParamType::PlatformType(ty) => match ty { 148 | ParamType::Bool => true, 149 | ParamType::I32 => true, 150 | ParamType::F64 => true, 151 | ParamType::String => true, 152 | ParamType::Date => false, 153 | ParamType::Blob => false, 154 | }, 155 | }; 156 | 157 | if arg_meta.default.is_some() && !allowed_defaults { 158 | return Err(Self::Error::TypeCannotBeDefault( 159 | arg_meta.ty, 160 | arg_meta.default.span(), 161 | )); 162 | } 163 | 164 | // if you pass "some_string" as default, it would get parsed by darling as `Ident` 165 | let default_fixed = arg_meta.default.map(|d| match d { 166 | Meta::NameValue(nv) => Ok(nv.value.to_token_stream()), 167 | _ => Err(Self::Error::UnexpectedMetaType(arg_meta.ident.span())), 168 | }); 169 | let default_fixed = default_fixed.transpose()?; 170 | 171 | Ok(Self { 172 | ty: arg_meta.ty, 173 | default: default_fixed, 174 | out_param: arg_meta.as_out.is_some(), 175 | }) 176 | } 177 | } 178 | 179 | #[derive(FromMeta, Debug)] 180 | struct FuncReturnMeta { 181 | ty: Option, 182 | result: Option<()>, 183 | } 184 | 185 | impl From for ReturnTypeDesc { 186 | fn from(arg_meta: FuncReturnMeta) -> Self { 187 | Self { 188 | ty: arg_meta.ty, 189 | result: arg_meta.result.is_some(), 190 | } 191 | } 192 | } 193 | 194 | pub enum ErrorConvertingMeta { 195 | UnexpectedMetaType(Span), 196 | TypeCannotBeDefault(FuncParamType, Span), 197 | InvalidTypeForParam(Span, String), 198 | InvalidTypeForReturn(Span, String), 199 | ConflictingParams(Span, String, String), 200 | } 201 | 202 | impl From for darling::Error { 203 | fn from(err: ErrorConvertingMeta) -> Self { 204 | match err { 205 | ErrorConvertingMeta::InvalidTypeForParam(span, ty) => { 206 | let joined_allowed_types = crate::derive_addin::constants::ALL_ARG_TYPES.join(", "); 207 | darling::Error::custom(format!( 208 | "Invalid type: `{ty}`. Must be one of: {joined_allowed_types}" 209 | )) 210 | .with_span(&span) 211 | } 212 | ErrorConvertingMeta::InvalidTypeForReturn(span, ty) => { 213 | let joined_allowed_types = 214 | crate::derive_addin::constants::ALL_RETURN_TYPES.join(", "); 215 | darling::Error::custom(format!( 216 | "Invalid type: `{ty}`. Must be one of: {joined_allowed_types}" 217 | )) 218 | .with_span(&span) 219 | } 220 | ErrorConvertingMeta::ConflictingParams(span, param1, param2) => { 221 | darling::Error::custom(format!("Conflicting params: {} and {}", param1, param2)) 222 | .with_span(&span) 223 | } 224 | ErrorConvertingMeta::TypeCannotBeDefault(param_type, span) => { 225 | darling::Error::custom(format!("Type `{param_type}` cannot have default value")) 226 | .with_span(&span) 227 | } 228 | ErrorConvertingMeta::UnexpectedMetaType(span) => { 229 | darling::Error::custom("Unexpected meta type").with_span(&span) 230 | } 231 | } 232 | } 233 | } 234 | 235 | pub fn parse_functions(struct_data: &DataStruct) -> Result, darling::Error> { 236 | let mut functions_descriptions = vec![]; 237 | 238 | // iterate over methods 239 | for field in &struct_data.fields { 240 | let has_add_in_func_attr = field 241 | .attrs 242 | .iter() 243 | .any(|attr| attr.path().is_ident("add_in_func")); 244 | if !has_add_in_func_attr { 245 | continue; 246 | }; 247 | 248 | let func_desc = FuncDesc::from_field(field)?; 249 | functions_descriptions.push(func_desc); 250 | } 251 | 252 | Ok(functions_descriptions) 253 | } 254 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/mod.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | use functions::{collectors::*, parse::parse_functions}; 6 | use props::{collectors::*, parse::parse_props}; 7 | use utils::{macros::tkn_err, str_literal_token}; 8 | 9 | mod constants; 10 | mod functions; 11 | mod parsers; 12 | mod props; 13 | mod utils; 14 | 15 | pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 16 | let derive_input = parse_macro_input!(input as DeriveInput); 17 | match derive_result(&derive_input) { 18 | Ok(tokens) => tokens.into(), 19 | Err(tokens) => tokens.into(), 20 | } 21 | } 22 | 23 | fn derive_result(input: &DeriveInput) -> Result { 24 | let impl_block = build_impl_block(input).map_err(|darling_error| { 25 | let error_tokens = darling_error.write_errors(); 26 | let error_tokens = quote! { 27 | compile_error!(#error_tokens); 28 | }; 29 | error_tokens 30 | })?; 31 | 32 | Ok(quote! { 33 | #impl_block 34 | }) 35 | } 36 | 37 | fn build_impl_block(input: &DeriveInput) -> Result { 38 | let struct_ident = &input.ident; 39 | let syn::Data::Struct(struct_data) = &input.data else { 40 | return tkn_err!( 41 | "AddIn can only be derived for structs", 42 | &struct_ident.span() 43 | ); 44 | }; 45 | let add_in_name_literal = str_literal_token(&struct_ident.to_string(), struct_ident)?; 46 | 47 | let props = parse_props(struct_data)?; 48 | let functions = parse_functions(struct_data)?; 49 | 50 | let pi = props.iter().enumerate(); 51 | let prop_definitions = [ 52 | pi.clone().collect::().release()?, 53 | pi.clone().collect::().release()?, 54 | pi.clone().collect::().release()?, 55 | pi.clone().collect::().release()?, 56 | pi.clone().collect::().release()?, 57 | pi.clone().collect::().release()?, 58 | pi.clone().collect::().release()?, 59 | ]; 60 | 61 | let fi = functions.iter().enumerate(); 62 | let func_definitions = [ 63 | fi.clone().collect::().release()?, 64 | fi.clone().collect::().release()?, 65 | fi.clone().collect::().release()?, 66 | fi.clone().collect::().release()?, 67 | fi.clone().collect::().release()?, 68 | fi.clone().collect::().release()?, 69 | fi.clone().collect::().release()?, 70 | fi.clone() 71 | .collect::() 72 | .release()?, 73 | ]; 74 | 75 | let result = quote! { 76 | impl native_api_1c::native_api_1c_core::interface::AddInWrapper for #struct_ident { 77 | fn init(&mut self, interface: &'static native_api_1c::native_api_1c_core::ffi::connection::Connection) -> bool { 78 | self.connection = std::sync::Arc::new(Some(interface)); 79 | true 80 | } 81 | 82 | fn get_info(&self) -> u16 { 83 | 2000 84 | } 85 | fn done(&mut self) {} 86 | fn register_extension_as(&mut self) -> &[u16] { 87 | &utf16_lit::utf16_null!(#add_in_name_literal) 88 | } 89 | 90 | #(#prop_definitions)* 91 | #(#func_definitions)* 92 | 93 | fn set_locale(&mut self, loc: &[u16]) { 94 | } 95 | fn set_user_interface_language_code(&mut self, lang: &[u16]) { 96 | } 97 | } 98 | }; 99 | Ok(result) 100 | } 101 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/parsers.rs: -------------------------------------------------------------------------------- 1 | use darling::FromMeta; 2 | use proc_macro2::TokenStream; 3 | use quote::{quote, ToTokens}; 4 | 5 | use super::constants::{BLOB_TYPE, BOOL_TYPE, DATE_TYPE, F64_TYPE, I32_TYPE, STRING_TYPE}; 6 | 7 | #[derive(Clone, Debug, PartialEq)] 8 | pub enum ParamType { 9 | Bool, 10 | I32, 11 | F64, 12 | String, 13 | Date, 14 | Blob, 15 | } 16 | 17 | const META_TYPE_ERR: &str = "expected string literal or path"; 18 | 19 | impl FromMeta for ParamType { 20 | fn from_expr(expr: &syn::Expr) -> darling::Result { 21 | let meta_type_err = darling::Error::custom(META_TYPE_ERR); 22 | let expr_string = match expr { 23 | syn::Expr::Lit(str_lit) => match str_lit.lit { 24 | syn::Lit::Str(ref str) => str.value(), 25 | _ => return Err(meta_type_err), 26 | }, 27 | syn::Expr::Path(path) => path.path.segments.first().unwrap().ident.to_string(), 28 | _ => return Err(meta_type_err), 29 | }; 30 | Self::from_string(&expr_string) 31 | } 32 | 33 | fn from_string(value: &str) -> darling::Result { 34 | let joined_allowed_types = crate::derive_addin::constants::ALL_ARG_TYPES.join(", "); 35 | Self::try_from(value).map_err(|_| { 36 | darling::Error::custom(format!( 37 | "unknown type `{value}`. Must be one of: {joined_allowed_types}", 38 | )) 39 | }) 40 | } 41 | } 42 | 43 | impl TryFrom<&str> for ParamType { 44 | type Error = (); 45 | 46 | fn try_from(value: &str) -> Result { 47 | match value { 48 | BOOL_TYPE => Ok(ParamType::Bool), 49 | I32_TYPE => Ok(ParamType::I32), 50 | F64_TYPE => Ok(ParamType::F64), 51 | STRING_TYPE => Ok(ParamType::String), 52 | DATE_TYPE => Ok(ParamType::Date), 53 | BLOB_TYPE => Ok(ParamType::Blob), 54 | _ => Err(()), 55 | } 56 | } 57 | } 58 | 59 | impl ToTokens for ParamType { 60 | fn to_tokens(&self, tokens: &mut TokenStream) { 61 | *tokens = match self { 62 | ParamType::Bool => { 63 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Bool } 64 | } 65 | ParamType::I32 => { 66 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::I32 } 67 | } 68 | ParamType::F64 => { 69 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::F64 } 70 | } 71 | ParamType::Date => { 72 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Date } 73 | } 74 | ParamType::String => { 75 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::String } 76 | } 77 | ParamType::Blob => { 78 | quote! { native_api_1c::native_api_1c_core::interface::ParamValue::Blob } 79 | } 80 | } 81 | } 82 | } 83 | 84 | #[derive(Debug)] 85 | pub enum PropName { 86 | StringLiteral(syn::LitStr), 87 | Ident(syn::ExprPath), 88 | } 89 | 90 | impl FromMeta for PropName { 91 | fn from_expr(expr: &syn::Expr) -> darling::Result { 92 | match expr { 93 | syn::Expr::Lit(lit) => match &lit.lit { 94 | syn::Lit::Str(str_lit) => Ok(PropName::StringLiteral(str_lit.clone())), 95 | _ => Err(darling::Error::custom("expected string literal").with_span(expr)), 96 | }, 97 | syn::Expr::Path(path) => Ok(PropName::Ident(path.clone())), 98 | _ => Err(darling::Error::custom("expected string literal or path").with_span(expr)), 99 | } 100 | } 101 | } 102 | 103 | impl From for proc_macro2::TokenStream { 104 | fn from(prop_name: PropName) -> proc_macro2::TokenStream { 105 | match prop_name { 106 | PropName::StringLiteral(str_lit) => str_lit.to_token_stream(), 107 | PropName::Ident(ident) => ident.to_token_stream(), 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/find_prop.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::props::PropDesc; 5 | 6 | use super::{empty_prop_collector_error, PropCollector}; 7 | 8 | pub struct FindPropCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for FindPropCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_prop_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a PropDesc)> for FindPropCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut find_prop_body = TokenStream::new(); 23 | 24 | for (prop_index, prop_desc) in iter { 25 | let name_literal = prop_desc.name_literal.clone(); 26 | let name_ru_literal = prop_desc.name_ru_literal.clone(); 27 | 28 | find_prop_body.extend(quote! { 29 | if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil(#name_literal) == name { 30 | return Some(#prop_index) 31 | }; 32 | if native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil(#name_ru_literal) == name { 33 | return Some(#prop_index) 34 | }; 35 | }); 36 | } 37 | 38 | let _definition = quote! { 39 | fn find_prop(&self, name: &[u16]) -> Option { 40 | #find_prop_body 41 | None 42 | } 43 | }; 44 | 45 | Self { 46 | generated: Ok(_definition), 47 | } 48 | } 49 | } 50 | 51 | impl PropCollector<'_> for FindPropCollector { 52 | fn release(self) -> Result { 53 | self.generated 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/get_n_props.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::props::PropDesc; 5 | 6 | use super::{empty_prop_collector_error, PropCollector}; 7 | 8 | pub struct GetNPropsCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for GetNPropsCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_prop_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a PropDesc)> for GetNPropsCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut _body = TokenStream::new(); 23 | 24 | let number_of_props = iter.into_iter().count(); 25 | 26 | let _definition = quote! { 27 | fn get_n_props(&self) -> usize { 28 | #number_of_props 29 | } 30 | }; 31 | 32 | Self { 33 | generated: Ok(_definition), 34 | } 35 | } 36 | } 37 | 38 | impl PropCollector<'_> for GetNPropsCollector { 39 | fn release(self) -> Result { 40 | self.generated 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/get_prop_name.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::props::PropDesc; 5 | 6 | use super::{empty_prop_collector_error, PropCollector}; 7 | 8 | pub struct GetPropNameCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for GetPropNameCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_prop_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a PropDesc)> for GetPropNameCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut get_prop_name_body = TokenStream::new(); 23 | 24 | for (prop_index, prop_desc) in iter { 25 | let name_literal = prop_desc.name_literal.clone(); 26 | let name_ru_literal = prop_desc.name_ru_literal.clone(); 27 | 28 | get_prop_name_body.extend(quote! { 29 | if num == #prop_index && alias == 0 { return Some(native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil(#name_literal).into()) }; 30 | if num == #prop_index { return Some(native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil(#name_ru_literal).into()) }; 31 | }); 32 | } 33 | 34 | let _definition = quote! { 35 | fn get_prop_name(&self, num: usize, alias: usize) -> Option> { 36 | #get_prop_name_body 37 | None 38 | } 39 | }; 40 | 41 | Self { 42 | generated: Ok(_definition), 43 | } 44 | } 45 | } 46 | 47 | impl PropCollector<'_> for GetPropNameCollector { 48 | fn release(self) -> Result { 49 | self.generated 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/get_prop_val.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::{props::PropDesc, utils::expr_to_os_value}; 5 | 6 | use super::{empty_prop_collector_error, PropCollector}; 7 | 8 | pub struct GetPropValCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for GetPropValCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_prop_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a PropDesc)> for GetPropValCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut body = TokenStream::new(); 23 | 24 | for (prop_index, prop_desc) in iter { 25 | if !prop_desc.readable { 26 | // Skip non-readable properties 27 | continue; 28 | } 29 | 30 | let prop_ident = &prop_desc.ident; 31 | let prop_setter = expr_to_os_value("e! {self.#prop_ident}, &prop_desc.ty, false); 32 | body.extend(quote! { 33 | if num == #prop_index { 34 | return Ok(#prop_setter); 35 | }; 36 | }); 37 | } 38 | 39 | let definition = quote! { 40 | fn get_prop_val(&self, num: usize) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult< 41 | native_api_1c::native_api_1c_core::interface::ParamValue 42 | > { 43 | #body 44 | return Err(()) 45 | } 46 | }; 47 | 48 | Self { 49 | generated: Ok(definition), 50 | } 51 | } 52 | } 53 | 54 | impl PropCollector<'_> for GetPropValCollector { 55 | fn release(self) -> Result { 56 | self.generated 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/is_prop_readable.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::props::PropDesc; 5 | 6 | use super::{empty_prop_collector_error, PropCollector}; 7 | 8 | pub struct IsPropReadableCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for IsPropReadableCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_prop_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a PropDesc)> for IsPropReadableCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut is_prop_readable_body = TokenStream::new(); 23 | 24 | for (prop_index, prop_desc) in iter { 25 | let readable = prop_desc.readable; 26 | 27 | is_prop_readable_body.extend(quote! { 28 | if num == #prop_index { return #readable }; 29 | }); 30 | } 31 | 32 | let _definition = quote! { 33 | fn is_prop_readable(&self, num: usize) -> bool { 34 | #is_prop_readable_body 35 | false 36 | } 37 | }; 38 | 39 | Self { 40 | generated: Ok(_definition), 41 | } 42 | } 43 | } 44 | 45 | impl PropCollector<'_> for IsPropReadableCollector { 46 | fn release(self) -> Result { 47 | self.generated 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/is_prop_writable.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::props::PropDesc; 5 | 6 | use super::{empty_prop_collector_error, PropCollector}; 7 | 8 | pub struct IsPropWritableCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for IsPropWritableCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_prop_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a PropDesc)> for IsPropWritableCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut is_prop_writable_body = TokenStream::new(); 23 | 24 | for (prop_index, prop_desc) in iter { 25 | let writable = prop_desc.writable; 26 | is_prop_writable_body.extend(quote! { 27 | if num == #prop_index { return #writable }; 28 | }); 29 | } 30 | 31 | let _definition = quote! { 32 | fn is_prop_writable(&self, num: usize) -> bool { 33 | #is_prop_writable_body 34 | false 35 | } 36 | }; 37 | 38 | Self { 39 | generated: Ok(_definition), 40 | } 41 | } 42 | } 43 | 44 | impl PropCollector<'_> for IsPropWritableCollector { 45 | fn release(self) -> Result { 46 | self.generated 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/mod.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | 3 | use super::PropDesc; 4 | use crate::derive_addin::utils::macros::tkn_err_inner; 5 | 6 | pub mod find_prop; 7 | pub mod get_n_props; 8 | pub mod get_prop_name; 9 | pub mod get_prop_val; 10 | pub mod is_prop_readable; 11 | pub mod is_prop_writable; 12 | pub mod set_prop_val; 13 | 14 | pub use find_prop::FindPropCollector; 15 | pub use get_n_props::GetNPropsCollector; 16 | pub use get_prop_name::GetPropNameCollector; 17 | pub use get_prop_val::GetPropValCollector; 18 | pub use is_prop_readable::IsPropReadableCollector; 19 | pub use is_prop_writable::IsPropWritableCollector; 20 | pub use set_prop_val::SetPropValCollector; 21 | 22 | pub trait PropCollector<'a>: FromIterator<(usize, &'a PropDesc)> + Default { 23 | fn release(self) -> Result; 24 | } 25 | 26 | pub fn empty_prop_collector_error() -> darling::Error { 27 | tkn_err_inner!("No props found", &proc_macro2::Span::call_site()) 28 | } 29 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/collectors/set_prop_val.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::derive_addin::{props::PropDesc, utils::expr_from_os_value}; 5 | 6 | use super::{empty_prop_collector_error, PropCollector}; 7 | 8 | pub struct SetPropValCollector { 9 | generated: Result, 10 | } 11 | 12 | impl Default for SetPropValCollector { 13 | fn default() -> Self { 14 | Self { 15 | generated: Err(empty_prop_collector_error()), 16 | } 17 | } 18 | } 19 | 20 | impl<'a> FromIterator<(usize, &'a PropDesc)> for SetPropValCollector { 21 | fn from_iter>(iter: T) -> Self { 22 | let mut body = TokenStream::new(); 23 | 24 | for (prop_index, prop_desc) in iter { 25 | if !prop_desc.writable { 26 | continue; 27 | } 28 | 29 | let prop_ident = &prop_desc.ident; 30 | let prop_getter = expr_from_os_value("e! { val }, &prop_desc.ty); 31 | 32 | body.extend(quote! { 33 | if num == #prop_index { 34 | self.#prop_ident = #prop_getter.into(); 35 | return Ok(()); 36 | }; 37 | }); 38 | } 39 | 40 | let definition = quote! { 41 | fn set_prop_val( 42 | &mut self, 43 | num: usize, 44 | val: native_api_1c::native_api_1c_core::interface::ParamValue, 45 | ) -> native_api_1c::native_api_1c_core::interface::AddInWrapperResult<()> { 46 | #body 47 | return Err(()) 48 | } 49 | }; 50 | 51 | Self { 52 | generated: Ok(definition), 53 | } 54 | } 55 | } 56 | 57 | impl PropCollector<'_> for SetPropValCollector { 58 | fn release(self) -> Result { 59 | self.generated 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/generate.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/mod.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, TokenStream}; 2 | 3 | use super::parsers::ParamType; 4 | 5 | pub mod collectors; 6 | pub mod generate; 7 | pub mod parse; 8 | 9 | #[derive(Debug)] 10 | pub struct PropDesc { 11 | pub ident: Ident, 12 | 13 | pub name_literal: TokenStream, 14 | pub name_ru_literal: TokenStream, 15 | 16 | pub readable: bool, 17 | pub writable: bool, 18 | pub ty: ParamType, 19 | } 20 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/props/parse.rs: -------------------------------------------------------------------------------- 1 | use darling::{FromField, FromMeta}; 2 | 3 | use syn::{Attribute, DataStruct}; 4 | 5 | use crate::derive_addin::{parsers::PropName, utils::ident_option_to_darling_err}; 6 | 7 | use super::{ParamType, PropDesc}; 8 | 9 | impl FromField for PropDesc { 10 | fn from_field(field: &syn::Field) -> darling::Result { 11 | let field_ident = ident_option_to_darling_err(field.ident.as_ref())?; 12 | 13 | let add_in_prop_attr: Vec<&Attribute> = field 14 | .attrs 15 | .iter() 16 | .filter(|attr| attr.path().is_ident("add_in_prop")) 17 | .collect(); 18 | if add_in_prop_attr.is_empty() { 19 | return Err( 20 | darling::Error::custom("Field must have `add_in_prop` attribute") 21 | .with_span(&field_ident.clone()), 22 | ); 23 | } else if add_in_prop_attr.len() > 1 { 24 | return Err( 25 | darling::Error::custom("Field can have only 1 `add_in_prop` attribute") 26 | .with_span(&field_ident.clone()), 27 | ); 28 | }; 29 | let add_in_prop_attr = add_in_prop_attr[0]; 30 | 31 | let prop_meta = PropMeta::from_meta(&add_in_prop_attr.meta)?; 32 | 33 | Ok(Self { 34 | ident: field_ident.clone(), 35 | 36 | name_literal: prop_meta.name.into(), 37 | name_ru_literal: prop_meta.name_ru.into(), 38 | 39 | readable: prop_meta.readable.is_some(), 40 | writable: prop_meta.writable.is_some(), 41 | ty: prop_meta.ty, 42 | }) 43 | } 44 | } 45 | 46 | #[derive(FromMeta, Debug)] 47 | pub struct PropMeta { 48 | pub ty: ParamType, 49 | pub name: PropName, 50 | pub name_ru: PropName, 51 | pub readable: Option<()>, 52 | pub writable: Option<()>, 53 | } 54 | 55 | pub fn parse_props(struct_data: &DataStruct) -> Result, darling::Error> { 56 | let mut props = vec![]; 57 | 58 | for field in &struct_data.fields { 59 | let has_add_in_prop_attr = field 60 | .attrs 61 | .iter() 62 | .any(|attr| attr.path().is_ident("add_in_prop")); 63 | if !has_add_in_prop_attr { 64 | continue; 65 | }; 66 | 67 | let prop_desc = PropDesc::from_field(field)?; 68 | props.push(prop_desc); 69 | } 70 | 71 | Ok(props) 72 | } 73 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/derive_addin/utils.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{LexError, TokenStream}; 2 | use quote::quote; 3 | use syn::{spanned::Spanned, Ident}; 4 | 5 | use super::parsers::ParamType; 6 | 7 | pub mod macros { 8 | macro_rules! tkn_err_inner { 9 | ($str:expr, $span:expr) => {{ 10 | let err_inner: darling::Error = darling::Error::custom($str).with_span($span); 11 | err_inner 12 | }}; 13 | } 14 | 15 | macro_rules! tkn_err { 16 | ($str:expr, $span:expr) => { 17 | Err(crate::derive_addin::utils::macros::tkn_err_inner!( 18 | $str, $span 19 | )) 20 | }; 21 | } 22 | 23 | pub(crate) use tkn_err; 24 | pub(crate) use tkn_err_inner; 25 | } 26 | 27 | const IDENT_OPTION_ERR: &str = "Unable to get ident from option"; 28 | 29 | pub fn ident_option_to_darling_err(ident: Option<&Ident>) -> Result<&Ident, darling::Error> { 30 | ident.ok_or_else(|| darling::Error::custom(IDENT_OPTION_ERR)) 31 | } 32 | 33 | pub fn str_literal_token( 34 | str_literal: &str, 35 | err_ident: &T, 36 | ) -> Result 37 | where 38 | T: Spanned, 39 | { 40 | format!(r#""{}""#, str_literal) 41 | .parse() 42 | .map_err(|e: LexError| { 43 | darling::Error::custom(format!("Unable to parse string literal: {}", e)) 44 | .with_span(err_ident) 45 | }) 46 | } 47 | 48 | pub fn expr_to_os_value( 49 | expr: &TokenStream, 50 | ty: &ParamType, 51 | string_nil: bool, 52 | ) -> proc_macro2::TokenStream { 53 | let os_string_fn = if string_nil { 54 | quote! {native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil} 55 | } else { 56 | quote! {native_api_1c::native_api_1c_core::ffi::string_utils::os_string} 57 | }; 58 | match ty { 59 | ParamType::String => quote! { 60 | { 61 | let _ = "expr_to_os_value: specific case for String"; 62 | #ty(#os_string_fn(&#expr.clone()).clone().into()) 63 | } 64 | }, 65 | _ => quote! { 66 | { 67 | let _ = "expr_to_os_value: generic case"; 68 | #ty(#expr.clone().into()) 69 | } 70 | }, 71 | } 72 | } 73 | 74 | pub fn expr_from_os_value(expr: &TokenStream, ty: &ParamType) -> proc_macro2::TokenStream { 75 | match ty { 76 | ParamType::String => quote! { 77 | { 78 | let _ = "expr_from_os_value: specific case for String"; 79 | match &#expr { 80 | #ty(val) => { 81 | Ok(native_api_1c::native_api_1c_core::ffi::string_utils::from_os_string(&val)) 82 | }, 83 | _ => Err(()), 84 | }?.clone() 85 | } 86 | }, 87 | ParamType::Blob => quote! { 88 | { 89 | let _ = "expr_from_os_value: specific case for Blob"; 90 | match &#expr { 91 | #ty(val) => { 92 | Ok(val) 93 | }, 94 | _ => Err(()), 95 | }?.clone() 96 | } 97 | }, 98 | _ => quote! { 99 | { 100 | let _ = "expr_from_os_value: generic case"; 101 | match #expr { 102 | #ty(val) => { 103 | Ok(val) 104 | }, 105 | _ => Err(()), 106 | }?.clone() 107 | } 108 | }, 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/extern_functions/mod.rs: -------------------------------------------------------------------------------- 1 | mod parse; 2 | 3 | use proc_macro2::{Span, TokenStream}; 4 | use quote::{quote, ToTokens}; 5 | 6 | use parse::ExternAddInsDesc; 7 | use syn::{LitByte, LitStr}; 8 | 9 | static ASCII_LOWER: [char; 50] = [ 10 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // numbers 11 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', // ASCII uppercase 12 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', // ASCII uppercase 13 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', // ASCII lowercase 14 | 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', // ASCII lowercase 15 | ]; 16 | 17 | pub fn extern_functions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 18 | let extern_add_ins = syn::parse_macro_input!(input as ExternAddInsDesc); 19 | 20 | if extern_add_ins.components.len() > ASCII_LOWER.len() { 21 | return quote! { 22 | compile_error!("Too many components, max is {}", ASCII_LOWER.len()); 23 | } 24 | .into(); 25 | } 26 | 27 | let mut get_class_object_body = TokenStream::new(); 28 | for (i, add_in_desc) in extern_add_ins.components.iter().enumerate() { 29 | let alias = ASCII_LOWER[i]; 30 | let alias_literal = LitByte::new(alias as u8, Span::call_site()); 31 | let alias_literal = alias_literal.to_token_stream(); 32 | let init_tkn = &add_in_desc.init_tkn; 33 | 34 | get_class_object_body.extend(quote! { 35 | #alias_literal => { 36 | let add_in = #init_tkn; 37 | native_api_1c::native_api_1c_core::ffi::create_component(component, add_in) 38 | }, 39 | }) 40 | } 41 | let get_class_object_body = quote! { 42 | match *name as u8 { 43 | #get_class_object_body 44 | _ => 0, 45 | } 46 | }; 47 | 48 | let names = ASCII_LOWER[..extern_add_ins.components.len()] 49 | .iter() 50 | .map(char::to_string) 51 | .collect::>() 52 | .join("|"); 53 | let names_lit = LitStr::new(&names, Span::call_site()); 54 | let names_lit = names_lit.to_token_stream(); 55 | let get_class_names_body = quote! { utf16_lit::utf16_null!(#names_lit).as_ptr() }; 56 | 57 | let result = quote! { 58 | pub static mut PLATFORM_CAPABILITIES: std::sync::atomic::AtomicI32 = 59 | std::sync::atomic::AtomicI32::new(-1); 60 | 61 | #[allow(non_snake_case)] 62 | #[no_mangle] 63 | pub extern "C" fn GetAttachType() -> native_api_1c::native_api_1c_core::ffi::AttachType { 64 | native_api_1c::native_api_1c_core::ffi::AttachType::Any 65 | } 66 | 67 | #[allow(non_snake_case)] 68 | #[no_mangle] 69 | pub unsafe extern "C" fn DestroyObject(component: *mut *mut std::ffi::c_void) -> std::ffi::c_long { 70 | native_api_1c::native_api_1c_core::ffi::destroy_component(component) 71 | } 72 | 73 | #[allow(non_snake_case)] 74 | #[no_mangle] 75 | pub unsafe extern "C" fn GetClassObject( 76 | name: *const u16, 77 | component: *mut *mut std::ffi::c_void, 78 | ) -> std::ffi::c_long { 79 | #get_class_object_body 80 | } 81 | 82 | #[allow(non_snake_case)] 83 | #[no_mangle] 84 | pub extern "C" fn GetClassNames() -> *const u16 { 85 | #get_class_names_body 86 | } 87 | }; 88 | 89 | result.into() 90 | } 91 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/extern_functions/parse.rs: -------------------------------------------------------------------------------- 1 | use darling::FromMeta; 2 | use proc_macro2::TokenStream; 3 | use quote::ToTokens; 4 | use syn::{ 5 | parse::{Parse, Parser}, 6 | punctuated::Punctuated, 7 | spanned::Spanned, 8 | token::Comma, 9 | Attribute, 10 | }; 11 | 12 | #[derive(Debug)] 13 | pub struct ExternAddInsDesc { 14 | pub components: Vec, 15 | } 16 | 17 | impl Parse for ExternAddInsDesc { 18 | fn parse(input: syn::parse::ParseStream) -> syn::Result { 19 | let parser = Punctuated::::parse_terminated(input).unwrap(); 20 | 21 | let components = parser 22 | .into_iter() 23 | .map(|expr| { 24 | let input: proc_macro::TokenStream = expr.to_token_stream().into(); 25 | ExternAddInComponentDesc::parse.parse(input) 26 | }) 27 | .collect::>>()?; 28 | 29 | Ok(ExternAddInsDesc { components }) 30 | } 31 | } 32 | 33 | #[derive(FromMeta, Debug)] 34 | struct ExternAddInComponentMeta { 35 | #[darling(rename = "name")] 36 | name_override: Option, 37 | } 38 | 39 | #[derive(Debug)] 40 | pub struct ExternAddInComponentDesc { 41 | pub name_override: Option, 42 | pub init_tkn: TokenStream, 43 | } 44 | 45 | impl Parse for ExternAddInComponentDesc { 46 | fn parse(input: syn::parse::ParseStream) -> syn::Result { 47 | let attrs = input.call(Attribute::parse_outer).unwrap(); 48 | let add_in_component_attrs = attrs 49 | .iter() 50 | .filter(|attr| attr.path().is_ident("add_in_component")) 51 | .collect::>(); 52 | if add_in_component_attrs.len() > 1 { 53 | return Err(syn::Error::new( 54 | add_in_component_attrs[1].span(), 55 | "at most one `add_in_component` attribute is allowed", 56 | )); 57 | } 58 | let attr = add_in_component_attrs.first(); 59 | let addin_desc = attr.map(|attr| ExternAddInComponentMeta::from_meta(&attr.meta)); 60 | let addin_desc = match addin_desc { 61 | Some(Ok(desc)) => Some(desc), 62 | Some(Err(err)) => { 63 | return Err(syn::Error::new( 64 | add_in_component_attrs[1].span(), 65 | err.to_string(), 66 | )) 67 | } 68 | None => None, 69 | }; 70 | 71 | let init_tkn = input.call(TokenStream::parse).unwrap(); 72 | 73 | Ok(ExternAddInComponentDesc { 74 | name_override: addin_desc.and_then(|desc| desc.name_override), 75 | init_tkn, 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /native_api_1c_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod derive_addin; 2 | mod extern_functions; 3 | 4 | #[proc_macro_derive(AddIn, attributes(add_in_prop, add_in_func, add_in_con, arg, returns))] 5 | pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 6 | derive_addin::derive(input) 7 | } 8 | 9 | #[proc_macro] 10 | pub fn extern_functions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 11 | extern_functions::extern_functions(input) 12 | } 13 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/interface/functions.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::{ 4 | ffi::{ 5 | connection::Connection, 6 | string_utils::{os_string, os_string_nil}, 7 | }, 8 | interface::{AddInWrapper, ParamValue, ParamValues}, 9 | }; 10 | use native_api_1c_macro::AddIn; 11 | use rstest::{fixture, rstest}; 12 | 13 | const DEFAULT_VALUE: i32 = 12; 14 | const OUT_STR: &str = "world"; 15 | 16 | const FUNCTION_NAME_EN: &str = "Function"; 17 | const FUNCTION_NAME_RU: &str = "Функция"; 18 | 19 | const PROCEDURE_NAME_EN: &str = "Procedure"; 20 | const PROCEDURE_NAME_RU: &str = "Процедура"; 21 | 22 | const OUT_FUNCTION_NAME_EN: &str = "OutFunction"; 23 | const OUT_FUNCTION_NAME_RU: &str = "ВыводФункция"; 24 | 25 | const INVALID_NAME: &str = "Invalid"; 26 | 27 | #[derive(AddIn)] 28 | struct TestAddIn { 29 | #[add_in_con] 30 | connection: Arc>, 31 | 32 | pub storage: i32, 33 | 34 | #[add_in_func(name = FUNCTION_NAME_EN, name_ru = FUNCTION_NAME_RU)] 35 | #[arg(ty = Int)] 36 | #[arg(ty = Int, default = DEFAULT_VALUE)] 37 | #[returns(ty = Int, result)] 38 | pub function: fn(&Self, i32, i32) -> Result, 39 | 40 | #[add_in_func(name = PROCEDURE_NAME_EN, name_ru = PROCEDURE_NAME_RU)] 41 | #[arg(ty = Int)] 42 | #[arg(ty = Int, default = DEFAULT_VALUE)] 43 | pub procedure: fn(&mut Self, i32, i32), 44 | 45 | #[add_in_func(name = OUT_FUNCTION_NAME_EN, name_ru = OUT_FUNCTION_NAME_RU)] 46 | #[arg(ty = Str, as_out, default = OUT_STR)] 47 | pub out_function: fn(&mut String), 48 | } 49 | 50 | #[fixture] 51 | fn add_in() -> TestAddIn { 52 | TestAddIn { 53 | connection: Arc::new(None), 54 | storage: 0, 55 | function: |addin, a, b| Ok(a + b + addin.storage), 56 | procedure: |addin, a, b| { 57 | addin.storage = a + b; 58 | }, 59 | out_function: |out_str| { 60 | *out_str = format!("Hello, {out_str}!"); 61 | }, 62 | } 63 | } 64 | 65 | #[rstest] 66 | fn test_get_n_methods(add_in: TestAddIn) { 67 | assert_eq!(add_in.get_n_methods(), 3) 68 | } 69 | 70 | #[rstest] 71 | #[case(FUNCTION_NAME_EN, Some(0))] 72 | #[case(FUNCTION_NAME_RU, Some(0))] 73 | #[case(PROCEDURE_NAME_EN, Some(1))] 74 | #[case(PROCEDURE_NAME_RU, Some(1))] 75 | #[case(OUT_FUNCTION_NAME_EN, Some(2))] 76 | #[case(OUT_FUNCTION_NAME_RU, Some(2))] 77 | #[case(INVALID_NAME, None)] 78 | fn test_find_method(add_in: TestAddIn, #[case] name: &str, #[case] expected: Option) { 79 | use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; 80 | 81 | assert_eq!(add_in.find_method(&os_string_nil(name)), expected); 82 | } 83 | 84 | #[rstest] 85 | #[case(0, 0, Some(FUNCTION_NAME_EN))] 86 | #[case(0, 1, Some(FUNCTION_NAME_RU))] 87 | #[case(0, 42, Some(FUNCTION_NAME_RU))] 88 | #[case(1, 0, Some(PROCEDURE_NAME_EN))] 89 | #[case(1, 1, Some(PROCEDURE_NAME_RU))] 90 | #[case(1, 42, Some(PROCEDURE_NAME_RU))] 91 | #[case(2, 0, Some(OUT_FUNCTION_NAME_EN))] 92 | #[case(2, 1, Some(OUT_FUNCTION_NAME_RU))] 93 | #[case(2, 42, Some(OUT_FUNCTION_NAME_RU))] 94 | #[case(3, 0, None)] 95 | fn test_get_method_name( 96 | add_in: TestAddIn, 97 | #[case] method_i: usize, 98 | #[case] alias_i: usize, 99 | #[case] expected: Option<&str>, 100 | ) { 101 | use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; 102 | 103 | assert_eq!( 104 | add_in.get_method_name(method_i, alias_i), 105 | expected.map(os_string_nil) 106 | ); 107 | } 108 | 109 | #[rstest] 110 | #[case(0, 2)] 111 | #[case(1, 2)] 112 | #[case(2, 1)] 113 | #[case(3, 0)] 114 | fn test_get_n_params(add_in: TestAddIn, #[case] method_i: usize, #[case] n_params: usize) { 115 | assert_eq!(add_in.get_n_params(method_i), n_params); 116 | } 117 | 118 | #[rstest] 119 | #[case(0, 0, None)] 120 | #[case(0, 1, Some(ParamValue::I32(DEFAULT_VALUE)))] 121 | #[case(0, 42, None)] 122 | #[case(1, 0, None)] 123 | #[case(1, 1, Some(ParamValue::I32(DEFAULT_VALUE)))] 124 | #[case(1, 42, None)] 125 | #[case(2, 0, Some(ParamValue::String(os_string_nil(OUT_STR))))] 126 | #[case(2, 42, None)] 127 | #[case(3, 0, None)] 128 | fn test_get_param_def_value( 129 | add_in: TestAddIn, 130 | #[case] method_i: usize, 131 | #[case] param_i: usize, 132 | #[case] expected: Option, 133 | ) { 134 | assert_eq!(add_in.get_param_def_value(method_i, param_i), expected); 135 | } 136 | 137 | #[rstest] 138 | #[case(0, true)] 139 | #[case(1, false)] 140 | #[case(2, false)] 141 | #[case(3, false)] 142 | fn test_has_ret_val(add_in: TestAddIn, #[case] method_i: usize, #[case] has_ret_val: bool) { 143 | assert_eq!(add_in.has_ret_val(method_i), has_ret_val); 144 | } 145 | 146 | #[rstest] 147 | fn test_call_function(mut add_in: TestAddIn) { 148 | let a = ParamValue::I32(1); 149 | let b = ParamValue::I32(2); 150 | let mut params = ParamValues::new(vec![a, b]); 151 | 152 | let result = add_in.call_as_func(0, &mut params); 153 | assert!(result.is_ok()); 154 | assert_eq!(result.unwrap(), ParamValue::I32(1 + 2 + add_in.storage)); 155 | 156 | let result = add_in.call_as_proc(0, &mut params); 157 | assert!(result.is_ok()); 158 | } 159 | 160 | #[rstest] 161 | fn test_call_procedure(mut add_in: TestAddIn) { 162 | let a = ParamValue::I32(1); 163 | let b = ParamValue::I32(2); 164 | let mut params = ParamValues::new(vec![a, b]); 165 | 166 | let result = add_in.call_as_func(1, &mut params); 167 | assert!(result.is_err()); 168 | 169 | let result = add_in.call_as_proc(1, &mut params); 170 | assert!(result.is_ok()); 171 | assert_eq!(add_in.storage, 1 + 2); 172 | } 173 | 174 | #[rstest] 175 | fn test_call_out_function(mut add_in: TestAddIn) { 176 | let out_str = os_string("1C"); 177 | let mut params = ParamValues::new(vec![ParamValue::String(out_str)]); 178 | 179 | let result = add_in.call_as_func(2, &mut params); 180 | assert!(result.is_err()); 181 | 182 | let result = add_in.call_as_proc(2, &mut params); 183 | assert!(result.is_ok()); 184 | assert_eq!(params[0], ParamValue::String(os_string("Hello, 1C!"))); 185 | } 186 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/interface/props.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::{ 4 | ffi::connection::Connection, 5 | interface::{AddInWrapper, ParamValue}, 6 | }; 7 | use native_api_1c_macro::AddIn; 8 | use rstest::{fixture, rstest}; 9 | 10 | const RW_PROP_NAME: &str = "Property"; 11 | const RW_PROP_NAME_RU: &str = "Свойство"; 12 | 13 | const R_PROP_NAME: &str = "ReadOnlyProperty"; 14 | const R_PROP_NAME_RU: &str = "СвойствоТолькоЧтение"; 15 | 16 | const W_PROP_NAME: &str = "WriteOnlyProperty"; 17 | const W_PROP_NAME_RU: &str = "СвойствоТолькоЗапись"; 18 | 19 | const INVALID_PROP_NAME: &str = "InvalidProperty"; 20 | 21 | const START_VALUE: i32 = 42; 22 | const NEW_VALUE: i32 = 24; 23 | 24 | #[derive(AddIn)] 25 | struct TestAddIn { 26 | #[add_in_con] 27 | connection: Arc>, 28 | 29 | #[add_in_prop(ty = Int, name = RW_PROP_NAME, name_ru = RW_PROP_NAME_RU, readable, writable)] 30 | rw_property: i32, 31 | 32 | #[add_in_prop(ty = Int, name = R_PROP_NAME, name_ru = R_PROP_NAME_RU, readable)] 33 | r_property: i32, 34 | 35 | #[add_in_prop(ty = Int, name = W_PROP_NAME, name_ru = W_PROP_NAME_RU, writable)] 36 | w_property: i32, 37 | } 38 | 39 | #[fixture] 40 | fn add_in() -> TestAddIn { 41 | TestAddIn { 42 | connection: Arc::new(None), 43 | rw_property: START_VALUE, 44 | r_property: START_VALUE, 45 | w_property: START_VALUE, 46 | } 47 | } 48 | 49 | #[rstest] 50 | fn test_get_n_props(add_in: TestAddIn) { 51 | assert_eq!(add_in.get_n_props(), 3) 52 | } 53 | 54 | #[rstest] 55 | #[case(RW_PROP_NAME, Some(0))] 56 | #[case(RW_PROP_NAME_RU, Some(0))] 57 | #[case(R_PROP_NAME, Some(1))] 58 | #[case(R_PROP_NAME_RU, Some(1))] 59 | #[case(W_PROP_NAME, Some(2))] 60 | #[case(W_PROP_NAME_RU, Some(2))] 61 | #[case(INVALID_PROP_NAME, None)] 62 | fn test_find_prop(add_in: TestAddIn, #[case] prop_name: &str, #[case] prop_index: Option) { 63 | use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; 64 | 65 | assert_eq!(add_in.find_prop(&os_string_nil(prop_name)), prop_index); 66 | } 67 | 68 | #[rstest] 69 | #[case(0, 0, Some(RW_PROP_NAME))] 70 | #[case(0, 1, Some(RW_PROP_NAME_RU))] 71 | #[case(0, 43, Some(RW_PROP_NAME_RU))] 72 | #[case(1, 0, Some(R_PROP_NAME))] 73 | #[case(1, 1, Some(R_PROP_NAME_RU))] 74 | #[case(1, 43, Some(R_PROP_NAME_RU))] 75 | #[case(2, 0, Some(W_PROP_NAME))] 76 | #[case(2, 1, Some(W_PROP_NAME_RU))] 77 | #[case(2, 43, Some(W_PROP_NAME_RU))] 78 | fn test_get_prop_name( 79 | add_in: TestAddIn, 80 | #[case] prop_index: usize, 81 | #[case] name_index: usize, 82 | #[case] name: Option<&str>, 83 | ) { 84 | use native_api_1c::native_api_1c_core::ffi::string_utils::os_string_nil; 85 | 86 | let prop_name = add_in.get_prop_name(prop_index, name_index); 87 | assert_eq!(prop_name, name.map(os_string_nil)); 88 | } 89 | 90 | #[rstest] 91 | #[case(0, true)] 92 | #[case(1, true)] 93 | #[case(2, false)] 94 | fn test_is_prop_readable(add_in: TestAddIn, #[case] prop_index: usize, #[case] readable: bool) { 95 | assert_eq!(add_in.is_prop_readable(prop_index), readable); 96 | } 97 | 98 | #[rstest] 99 | #[case(0, true)] 100 | #[case(1, false)] 101 | #[case(2, true)] 102 | fn test_is_prop_writable(add_in: TestAddIn, #[case] prop_index: usize, #[case] writable: bool) { 103 | assert_eq!(add_in.is_prop_writable(prop_index), writable); 104 | } 105 | 106 | #[rstest] 107 | #[case(0, Some(START_VALUE))] 108 | #[case(1, Some(START_VALUE))] 109 | #[case(2, None)] 110 | fn test_get_prop_val( 111 | add_in: TestAddIn, 112 | #[case] prop_i: usize, 113 | #[case] expected_value: Option, 114 | ) { 115 | let prop_value = add_in.get_prop_val(prop_i); 116 | match expected_value { 117 | Some(value) => { 118 | assert!(prop_value.is_ok()); 119 | assert_eq!(prop_value.unwrap(), ParamValue::I32(value)); 120 | } 121 | None => assert!(prop_value.is_err()), 122 | } 123 | } 124 | 125 | #[rstest] 126 | #[case(0, Ok(()), |add_in: &TestAddIn| add_in.rw_property, NEW_VALUE)] 127 | #[case(1, Err(()), |add_in: &TestAddIn| add_in.r_property, START_VALUE)] 128 | #[case(2, Ok(()), |add_in: &TestAddIn| add_in.w_property, NEW_VALUE)] 129 | fn test_set_prop_val( 130 | mut add_in: TestAddIn, 131 | #[case] prop_i: usize, 132 | #[case] expected_result: Result<(), ()>, 133 | #[case] value_getter: fn(&TestAddIn) -> i32, 134 | #[case] new_value: i32, 135 | ) { 136 | assert_eq!( 137 | add_in.set_prop_val(prop_i, ParamValue::I32(NEW_VALUE)), 138 | expected_result 139 | ); 140 | assert_eq!(value_getter(&add_in), new_value); 141 | } 142 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn trybuild_props() { 3 | let t = trybuild::TestCases::new(); 4 | 5 | t.pass("tests/trybuild/to_build/props.rs"); 6 | } 7 | 8 | #[test] 9 | fn trybuild_functions() { 10 | let t = trybuild::TestCases::new(); 11 | 12 | t.pass("tests/trybuild/to_build/functions/bool_type.rs"); 13 | t.pass("tests/trybuild/to_build/functions/int_type.rs"); 14 | t.pass("tests/trybuild/to_build/functions/float_type.rs"); 15 | t.pass("tests/trybuild/to_build/functions/str_type.rs"); 16 | t.pass("tests/trybuild/to_build/functions/date_type.rs"); 17 | t.pass("tests/trybuild/to_build/functions/blob_type.rs"); 18 | 19 | t.pass("tests/trybuild/to_build/functions/result/bool_type.rs"); 20 | t.pass("tests/trybuild/to_build/functions/result/int_type.rs"); 21 | t.pass("tests/trybuild/to_build/functions/result/float_type.rs"); 22 | t.pass("tests/trybuild/to_build/functions/result/str_type.rs"); 23 | t.pass("tests/trybuild/to_build/functions/result/date_type.rs"); 24 | t.pass("tests/trybuild/to_build/functions/result/blob_type.rs"); 25 | 26 | t.pass("tests/trybuild/to_build/functions/out_params/bool_type.rs"); 27 | t.pass("tests/trybuild/to_build/functions/out_params/int_type.rs"); 28 | t.pass("tests/trybuild/to_build/functions/out_params/float_type.rs"); 29 | t.pass("tests/trybuild/to_build/functions/out_params/str_type.rs"); 30 | t.pass("tests/trybuild/to_build/functions/out_params/blob_type.rs"); 31 | 32 | t.pass("tests/trybuild/to_build/functions/defaults/bool_type.rs"); 33 | t.pass("tests/trybuild/to_build/functions/defaults/int_type.rs"); 34 | t.pass("tests/trybuild/to_build/functions/defaults/float_type.rs"); 35 | t.pass("tests/trybuild/to_build/functions/defaults/str_type.rs"); 36 | t.compile_fail("tests/trybuild/to_build/functions/defaults/date_type.rs"); 37 | t.compile_fail("tests/trybuild/to_build/functions/defaults/blob_type.rs"); 38 | } 39 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/blob_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Blob)] 13 | #[returns(ty = Blob)] 14 | pub my_function: fn(&Self, Vec) -> Vec, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: Vec) -> Vec { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/bool_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Bool)] 13 | #[returns(ty = Bool)] 14 | pub my_function: fn(&Self, bool) -> bool, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: bool) -> bool { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/date_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Date)] 13 | #[returns(ty = Date)] 14 | pub my_function: fn(&Self, chrono::NaiveDateTime) -> chrono::NaiveDateTime, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: chrono::NaiveDateTime) -> chrono::NaiveDateTime { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/blob_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Blob, default = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])] 13 | #[returns(ty = Blob)] 14 | pub my_function: fn(&Self, Vec) -> Vec, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: Vec) -> Vec { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/blob_type.stderr: -------------------------------------------------------------------------------- 1 | error: Type `Blob` cannot have default value 2 | --> tests/trybuild/to_build/functions/defaults/blob_type.rs:12:22 3 | | 4 | 12 | #[arg(ty = Blob, default = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])] 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/bool_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Bool, default = true)] 13 | #[returns(ty = Bool)] 14 | pub my_function: fn(&Self, bool) -> bool, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: bool) -> bool { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/date_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Date, default = "2021-01-01T00:00:00+00:00")] 13 | #[returns(ty = Date)] 14 | pub my_function: fn(&Self, chrono::NaiveDateTime) -> chrono::NaiveDateTime, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: chrono::NaiveDateTime) -> chrono::NaiveDateTime { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/date_type.stderr: -------------------------------------------------------------------------------- 1 | error: Type `Date` cannot have default value 2 | --> tests/trybuild/to_build/functions/defaults/date_type.rs:12:22 3 | | 4 | 12 | #[arg(ty = Date, default = "2021-01-01T00:00:00+00:00")] 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/float_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Float, default = 0.0)] 13 | #[returns(ty = Float)] 14 | pub my_function: fn(&Self, f64) -> f64, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: f64) -> f64 { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/int_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Int, default = 0)] 13 | #[returns(ty = Int)] 14 | pub my_function: fn(&Self, i32) -> i32, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: i32) -> i32 { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/defaults/str_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Str, default = "")] 13 | #[returns(ty = Str)] 14 | pub my_function: fn(&Self, String) -> String, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: String) -> String { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/float_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Float)] 13 | #[returns(ty = Float)] 14 | pub my_function: fn(&Self, f64) -> f64, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: f64) -> f64 { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/int_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Int)] 13 | #[returns(ty = Int)] 14 | pub my_function: fn(&Self, i32) -> i32, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: i32) -> i32 { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/out_params/blob_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunctionMut", name_ru = "МояФункцияМут")] 12 | #[arg(ty = Blob, as_in)] 13 | #[arg(ty = Blob, as_out)] 14 | #[returns(ty = Bool)] 15 | pub my_function_mut: fn(&mut Self, Vec, &mut Vec) -> bool, 16 | 17 | #[add_in_func(name = "MyFunctionRef", name_ru = "МояФункцияРеф")] 18 | #[arg(ty = Blob, as_in)] 19 | #[arg(ty = Blob, as_out)] 20 | #[returns(ty = Bool)] 21 | pub my_function_ref: fn(&Self, Vec, &mut Vec) -> bool, 22 | 23 | #[add_in_func(name = "MyFunctionNoRef", name_ru = "МояФункцияБезРеф")] 24 | #[arg(ty = Blob, as_in)] 25 | #[arg(ty = Blob, as_out)] 26 | #[returns(ty = Bool)] 27 | pub my_function_no_ref: fn(Vec, &mut Vec) -> bool, 28 | } 29 | 30 | impl MyAddIn { 31 | pub fn new() -> Self { 32 | Self { 33 | connection: Arc::new(None), 34 | my_function_mut: Self::my_function_mut_inner, 35 | my_function_ref: Self::my_function_ref_inner, 36 | my_function_no_ref: Self::my_function_no_ref_inner, 37 | } 38 | } 39 | 40 | fn my_function_mut_inner(&mut self, in_arg: Vec, out_arg: &mut Vec) -> bool { 41 | out_arg.extend_from_slice(&in_arg); 42 | true 43 | } 44 | 45 | fn my_function_ref_inner(&self, in_arg: Vec, out_arg: &mut Vec) -> bool { 46 | out_arg.extend_from_slice(&in_arg); 47 | true 48 | } 49 | 50 | fn my_function_no_ref_inner(in_arg: Vec, out_arg: &mut Vec) -> bool { 51 | out_arg.extend_from_slice(&in_arg); 52 | true 53 | } 54 | } 55 | 56 | fn main() { 57 | let _add_in = MyAddIn::new(); 58 | } 59 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/out_params/bool_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunctionMut", name_ru = "МояФункцияМут")] 12 | #[arg(ty = Bool, as_in)] 13 | #[arg(ty = Bool, as_out)] 14 | #[returns(ty = Bool)] 15 | pub my_function_mut: fn(&mut Self, bool, &mut bool) -> bool, 16 | 17 | #[add_in_func(name = "MyFunctionRef", name_ru = "МояФункцияРеф")] 18 | #[arg(ty = Bool, as_in)] 19 | #[arg(ty = Bool, as_out)] 20 | #[returns(ty = Bool)] 21 | pub my_function_ref: fn(&Self, bool, &mut bool) -> bool, 22 | 23 | #[add_in_func(name = "MyFunctionNoRef", name_ru = "МояФункцияБезРеф")] 24 | #[arg(ty = Bool, as_in)] 25 | #[arg(ty = Bool, as_out)] 26 | #[returns(ty = Bool)] 27 | pub my_function_no_ref: fn(bool, &mut bool) -> bool, 28 | } 29 | 30 | impl MyAddIn { 31 | pub fn new() -> Self { 32 | Self { 33 | connection: Arc::new(None), 34 | my_function_mut: Self::my_function_mut_inner, 35 | my_function_ref: Self::my_function_ref_inner, 36 | my_function_no_ref: Self::my_function_no_ref_inner, 37 | } 38 | } 39 | 40 | fn my_function_mut_inner(&mut self, in_arg: bool, out_arg: &mut bool) -> bool { 41 | *out_arg = !in_arg; 42 | true 43 | } 44 | 45 | fn my_function_ref_inner(&self, in_arg: bool, out_arg: &mut bool) -> bool { 46 | *out_arg = !in_arg; 47 | true 48 | } 49 | 50 | fn my_function_no_ref_inner(in_arg: bool, out_arg: &mut bool) -> bool { 51 | *out_arg = !in_arg; 52 | true 53 | } 54 | } 55 | 56 | fn main() { 57 | let _add_in = MyAddIn::new(); 58 | } 59 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/out_params/float_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunctionMut", name_ru = "МояФункцияМут")] 12 | #[arg(ty = Float, as_in)] 13 | #[arg(ty = Float, as_out)] 14 | #[returns(ty = Bool)] 15 | pub my_function_mut: fn(&mut Self, f64, &mut f64) -> bool, 16 | 17 | #[add_in_func(name = "MyFunctionRef", name_ru = "МояФункцияРеф")] 18 | #[arg(ty = Float, as_in)] 19 | #[arg(ty = Float, as_out)] 20 | #[returns(ty = Bool)] 21 | pub my_function_ref: fn(&Self, f64, &mut f64) -> bool, 22 | 23 | #[add_in_func(name = "MyFunctionNoRef", name_ru = "МояФункцияБезРеф")] 24 | #[arg(ty = Float, as_in)] 25 | #[arg(ty = Float, as_out)] 26 | #[returns(ty = Bool)] 27 | pub my_function_no_ref: fn(f64, &mut f64) -> bool, 28 | } 29 | 30 | impl MyAddIn { 31 | pub fn new() -> Self { 32 | Self { 33 | connection: Arc::new(None), 34 | my_function_mut: Self::my_function_mut_inner, 35 | my_function_ref: Self::my_function_ref_inner, 36 | my_function_no_ref: Self::my_function_no_ref_inner, 37 | } 38 | } 39 | 40 | fn my_function_mut_inner(&mut self, in_arg: f64, out_arg: &mut f64) -> bool { 41 | *out_arg = in_arg * 2.0; 42 | true 43 | } 44 | 45 | fn my_function_ref_inner(&self, in_arg: f64, out_arg: &mut f64) -> bool { 46 | *out_arg = in_arg * 2.0; 47 | true 48 | } 49 | 50 | fn my_function_no_ref_inner(in_arg: f64, out_arg: &mut f64) -> bool { 51 | *out_arg = in_arg * 2.0; 52 | true 53 | } 54 | } 55 | 56 | fn main() { 57 | let _add_in = MyAddIn::new(); 58 | } 59 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/out_params/int_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunctionMut", name_ru = "МояФункцияМут")] 12 | #[arg(ty = Int, as_in)] 13 | #[arg(ty = Int, as_out)] 14 | #[returns(ty = Bool)] 15 | pub my_function_mut: fn(&mut Self, i32, &mut i32) -> bool, 16 | 17 | #[add_in_func(name = "MyFunctionRef", name_ru = "МояФункцияРеф")] 18 | #[arg(ty = Int, as_in)] 19 | #[arg(ty = Int, as_out)] 20 | #[returns(ty = Bool)] 21 | pub my_function_ref: fn(&Self, i32, &mut i32) -> bool, 22 | 23 | #[add_in_func(name = "MyFunctionNoRef", name_ru = "МояФункцияБезРеф")] 24 | #[arg(ty = Int, as_in)] 25 | #[arg(ty = Int, as_out)] 26 | #[returns(ty = Bool)] 27 | pub my_function_no_ref: fn(i32, &mut i32) -> bool, 28 | } 29 | 30 | impl MyAddIn { 31 | pub fn new() -> Self { 32 | Self { 33 | connection: Arc::new(None), 34 | my_function_mut: Self::my_function_mut_inner, 35 | my_function_ref: Self::my_function_ref_inner, 36 | my_function_no_ref: Self::my_function_no_ref_inner, 37 | } 38 | } 39 | 40 | fn my_function_mut_inner(&mut self, in_arg: i32, out_arg: &mut i32) -> bool { 41 | *out_arg = in_arg * 2; 42 | true 43 | } 44 | 45 | fn my_function_ref_inner(&self, in_arg: i32, out_arg: &mut i32) -> bool { 46 | *out_arg = in_arg * 2; 47 | true 48 | } 49 | 50 | fn my_function_no_ref_inner(in_arg: i32, out_arg: &mut i32) -> bool { 51 | *out_arg = in_arg * 2; 52 | true 53 | } 54 | } 55 | 56 | fn main() { 57 | let _add_in = MyAddIn::new(); 58 | } 59 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/out_params/str_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunctionMut", name_ru = "МояФункцияМут")] 12 | #[arg(ty = Str, as_in)] 13 | #[arg(ty = Str, as_out)] 14 | #[returns(ty = Bool)] 15 | pub my_function_mut: fn(&mut Self, String, &mut String) -> bool, 16 | 17 | #[add_in_func(name = "MyFunctionRef", name_ru = "МояФункцияРеф")] 18 | #[arg(ty = Str, as_in)] 19 | #[arg(ty = Str, as_out)] 20 | #[returns(ty = Bool)] 21 | pub my_function_ref: fn(&Self, String, &mut String) -> bool, 22 | 23 | #[add_in_func(name = "MyFunctionNoRef", name_ru = "МояФункцияБезРеф")] 24 | #[arg(ty = Str, as_in)] 25 | #[arg(ty = Str, as_out)] 26 | #[returns(ty = Bool)] 27 | pub my_function_no_ref: fn(String, &mut String) -> bool, 28 | } 29 | 30 | impl MyAddIn { 31 | pub fn new() -> Self { 32 | Self { 33 | connection: Arc::new(None), 34 | my_function_mut: Self::my_function_mut_inner, 35 | my_function_ref: Self::my_function_ref_inner, 36 | my_function_no_ref: Self::my_function_no_ref_inner, 37 | } 38 | } 39 | 40 | fn my_function_mut_inner(&mut self, in_arg: String, out_arg: &mut String) -> bool { 41 | *out_arg = format!("in was: {}", in_arg); 42 | true 43 | } 44 | 45 | fn my_function_ref_inner(&self, in_arg: String, out_arg: &mut String) -> bool { 46 | *out_arg = format!("in was: {}", in_arg); 47 | true 48 | } 49 | 50 | fn my_function_no_ref_inner(in_arg: String, out_arg: &mut String) -> bool { 51 | *out_arg = format!("in was: {}", in_arg); 52 | true 53 | } 54 | } 55 | 56 | fn main() { 57 | let _add_in = MyAddIn::new(); 58 | } 59 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/result/blob_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Blob)] 13 | #[returns(ty = Blob, result)] 14 | pub my_function: fn(&Self, Vec) -> Result, ()>, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: Vec) -> Result, ()> { 26 | Ok(arg) 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/result/bool_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Bool)] 13 | #[returns(ty = Bool, result)] 14 | pub my_function: fn(&Self, bool) -> Result, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: bool) -> Result { 26 | Ok(arg) 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/result/date_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Date)] 13 | #[returns(ty = Date, result)] 14 | pub my_function: fn(&Self, chrono::NaiveDateTime) -> Result, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: chrono::NaiveDateTime) -> Result { 26 | Ok(arg) 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/result/float_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Float)] 13 | #[returns(ty = Float, result)] 14 | pub my_function: fn(&Self, f64) -> Result, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: f64) -> Result { 26 | Ok(arg) 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/result/int_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Int)] 13 | #[returns(ty = Int, result)] 14 | pub my_function: fn(&Self, i32) -> Result, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: i32) -> Result { 26 | Ok(arg) 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/result/str_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Str)] 13 | #[returns(ty = Str, result)] 14 | pub my_function: fn(&Self, String) -> Result, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: String) -> Result { 26 | Ok(arg) 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/functions/str_type.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 12 | #[arg(ty = Str)] 13 | #[returns(ty = Str)] 14 | pub my_function: fn(&Self, String) -> String, 15 | } 16 | 17 | impl MyAddIn { 18 | pub fn new() -> Self { 19 | Self { 20 | connection: Arc::new(None), 21 | my_function: Self::my_function_inner, 22 | } 23 | } 24 | 25 | fn my_function_inner(&self, arg: String) -> String { 26 | arg 27 | } 28 | } 29 | 30 | fn main() { 31 | let _add_in = MyAddIn::new(); 32 | } 33 | -------------------------------------------------------------------------------- /native_api_1c_macro/tests/trybuild/to_build/props.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::native_api_1c_core::ffi::connection::Connection; 4 | use native_api_1c_macro::AddIn; 5 | 6 | #[derive(AddIn)] 7 | pub struct MyAddIn { 8 | #[add_in_con] 9 | connection: Arc>, 10 | 11 | #[add_in_prop(ty = Str, name = "prp_RW_str", name_ru = "свств_RW_str", readable, writable)] 12 | pub str_prop_rw: String, 13 | #[add_in_prop(ty = Str, name = "prp_R_str", name_ru = "свств_R_str", readable)] 14 | pub str_prop_r: String, 15 | #[add_in_prop(ty = Str, name = "prp_W_str", name_ru = "свств_W_str", writable)] 16 | pub str_prop_w: String, 17 | 18 | #[add_in_prop(ty = Int, name = "prp_RW_int", name_ru = "свств_RW_int", readable, writable)] 19 | pub int_prop_rw: i32, 20 | #[add_in_prop(ty = Int, name = "prp_R_int", name_ru = "свств_R_int", readable)] 21 | pub int_prop_r: i32, 22 | #[add_in_prop(ty = Int, name = "prp_W_int", name_ru = "свств_W_int", writable)] 23 | pub int_prop_w: i32, 24 | 25 | #[add_in_prop(ty = Float, name = "prp_RW_float", name_ru = "свств_RW_float", readable, writable)] 26 | pub float_prop_rw: f64, 27 | #[add_in_prop(ty = Float, name = "prp_R_float", name_ru = "свств_R_float", readable)] 28 | pub float_prop_r: f64, 29 | #[add_in_prop(ty = Float, name = "prp_W_float", name_ru = "свств_W_float", writable)] 30 | pub float_prop_w: f64, 31 | 32 | #[add_in_prop(ty = Bool, name = "prp_RW_bool", name_ru = "свств_RW_bool", readable, writable)] 33 | pub bool_prop_rw: bool, 34 | #[add_in_prop(ty = Bool, name = "prp_R_bool", name_ru = "свств_R_bool", readable)] 35 | pub bool_prop_r: bool, 36 | #[add_in_prop(ty = Bool, name = "prp_W_bool", name_ru = "свств_W_bool", writable)] 37 | pub bool_prop_w: bool, 38 | 39 | #[add_in_prop(ty = Date, name = "prp_RW_date", name_ru = "свств_RW_date", readable, writable)] 40 | pub date_prop_rw: chrono::NaiveDateTime, 41 | #[add_in_prop(ty = Date, name = "prp_R_date", name_ru = "свств_R_date", readable)] 42 | pub date_prop_r: chrono::NaiveDateTime, 43 | #[add_in_prop(ty = Date, name = "prp_W_date", name_ru = "свств_W_date", writable)] 44 | pub date_prop_w: chrono::NaiveDateTime, 45 | 46 | #[add_in_prop(ty = Blob, name = "prp_RW_blob", name_ru = "свств_RW_blob", readable, writable)] 47 | pub blob_prop_rw: Vec, 48 | #[add_in_prop(ty = Blob, name = "prp_R_blob", name_ru = "свств_R_blob", readable)] 49 | pub blob_prop_r: Vec, 50 | #[add_in_prop(ty = Blob, name = "prp_W_blob", name_ru = "свств_W_blob", writable)] 51 | pub blob_prop_w: Vec, 52 | } 53 | 54 | impl MyAddIn { 55 | pub fn new() -> Self { 56 | Self { 57 | connection: Arc::new(None), 58 | str_prop_rw: String::new(), 59 | str_prop_r: String::new(), 60 | str_prop_w: String::new(), 61 | int_prop_rw: 0, 62 | int_prop_r: 0, 63 | int_prop_w: 0, 64 | float_prop_rw: 0.0, 65 | float_prop_r: 0.0, 66 | float_prop_w: 0.0, 67 | bool_prop_rw: false, 68 | bool_prop_r: false, 69 | bool_prop_w: false, 70 | date_prop_rw: chrono::NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), 71 | date_prop_r: chrono::NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), 72 | date_prop_w: chrono::NaiveDateTime::from_timestamp_opt(0, 0).unwrap(), 73 | blob_prop_rw: Vec::new(), 74 | blob_prop_r: Vec::new(), 75 | blob_prop_w: Vec::new(), 76 | } 77 | } 78 | } 79 | 80 | fn main() { 81 | let _add_in = MyAddIn::new(); 82 | } 83 | -------------------------------------------------------------------------------- /sample_addin_rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sample_addin_rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | utf16_lit = "2.0" 11 | native_api_1c = { path = "../native_api_1c" } 12 | chrono = "0.4.26" 13 | -------------------------------------------------------------------------------- /sample_addin_rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use native_api_1c::{ 4 | native_api_1c_core::ffi::connection::Connection, 5 | native_api_1c_macro::{extern_functions, AddIn}, 6 | }; 7 | 8 | #[derive(AddIn)] 9 | pub struct SampleAddIn { 10 | /// connection with 1C, used for calling events 11 | /// Arc is used to allow multiple threads to access the connection 12 | #[add_in_con] 13 | connection: Arc>, 14 | 15 | /// Property, readable and writable from 1C 16 | #[add_in_prop(ty = Int, name = "MyProp", name_ru = "МоеСвойство", readable, writable)] 17 | pub some_prop: i32, 18 | 19 | /// Property, readable from 1C but not writable 20 | #[add_in_prop(ty = Int, name = "ProtectedProp", name_ru = "ЗащищенноеСвойство", readable)] 21 | pub protected_prop: i32, 22 | 23 | /// Function, taking one or two arguments and returning a result 24 | /// In 1C it can be called as: 25 | /// ```bsl 26 | /// CompObj.MyFunction(10, 15); // 2nd arg = 15 27 | /// CompObj.MyFunction(10); // 2nd arg = 12 (default value) 28 | /// ``` 29 | /// If function returns an error, but does not panic, then 1C will throw an exception 30 | #[add_in_func(name = "MyFunction", name_ru = "МояФункция")] 31 | #[arg(ty = Int)] 32 | #[arg(ty = Int, default = 12)] // default value for the second argument 33 | #[returns(ty = Int, result)] 34 | pub my_function: fn(&Self, i32, i64) -> Result, 35 | 36 | /// Function, taking no arguments and returning nothing 37 | #[add_in_func(name = "MyProcedure", name_ru = "МояПроцедура")] 38 | pub my_procedure: fn(&mut Self), 39 | 40 | /// Private field, not visible from 1C 41 | private_field: i32, 42 | } 43 | 44 | impl Default for SampleAddIn { 45 | fn default() -> Self { 46 | Self { 47 | connection: Arc::new(None), 48 | some_prop: 0, 49 | protected_prop: 50, 50 | my_function: Self::my_function_inner, 51 | my_procedure: Self::my_procedure_inner, 52 | private_field: 100, 53 | } 54 | } 55 | } 56 | 57 | impl SampleAddIn { 58 | fn my_function_inner(&self, arg: i32, arg_maybe_default: i64) -> Result { 59 | Ok(self.protected_prop 60 | + self.some_prop 61 | + arg 62 | + self.private_field 63 | + arg_maybe_default as i32) 64 | } 65 | 66 | fn my_procedure_inner(&mut self) { 67 | self.protected_prop += 10; 68 | } 69 | } 70 | 71 | extern_functions! { 72 | SampleAddIn::default(), 73 | } 74 | --------------------------------------------------------------------------------