Гайд по использованию на русском языке можно посмотреть здесь и задать вопросы по использованию, но не оставляйте там комментарии об ошибках, т.к. там сложно это обсуждать. Лучше создайте issue в этом репозитории.
Disclaimer
This is my personal project, so there are some things coming from this fact:
- While I'm trying to implement everything in an idiomatic and 'pretty' way, sometimes I just want to see progress, so some cargo clippy warnings are ignored at times, but I always try to fix them later
- There'll be weeks or even months of inactivity, because I'm occupied with other things
- I'll try to help anyone, who opens issue or discussion, but I can't guarantee that I'll be able to do it in a timely manner
Contributing
I'd be glad to see any contributions, but please, follow these rules:
- If you want to add a feature, please, open an issue first, so we can discuss it. I don't want you to waste your time on something that I won't be accepting for one reason or another
- If you want to fix a bug, better do the same, but if it's a small bug, you can just open a PR
- If you want to help, but don't know what to do, you can look at issues with
help wantedlabel, or just ask in this Telegram chat
About
Library for simple 1C:Enterprise platform Native API Component development, originates from findings of this medigor/example-native-api-rs
Crate is tested on Linux and Windows. It should work on MacOS as well, but it is not tested.
A word on testing
In order to test the actual FFI calls, you need to have 1C:Enterprise file base, which makes it hard to test reliably with Rust tests, and also makes it not free :). One alternative is to use OneScript, which can run 1C:Enterprise scripts, including AddIn functions. However, it is not a perfect solution, because it is not 1C:Enterprise itself, and their implementations of Native API interfaces is not the same (See this issue or try building and running this example)
Structure
Library is divided into two submodules:
native_api_1c_coredescribes all necessary for implementing 1C:Enterprise Native APInative_api_1c_macroprovides a tool for significant simplification of component implementation, taking care ofnative_api_1c_core::interface::AddInWrapperproperty implementation
Usage
Attributes #[add_in_prop(...)]
name- property name in 1Cname_ru- property name in 1C in Russianreadable- property is readable from 1Cwritable- property is writable from 1C
Available property types: i32, f64, bool, String
Functions or procedures #[add_in_func(...)]
name- property name in 1Cname_ru- property name in 1C in Russian
Input arguments, #[arg(ty = ...)], for each type of argument must be set, on of:
| Type definition | Rust type | 1C type |
|---|---|---|
Int |
i32 |
Number (Int) |
Float |
f64 |
Number (Float or Int) |
Bool |
bool |
Boolean |
Str |
String |
String |
Date |
chrono::NaiveDateTime |
Date |
Blob |
Vec<u8> |
BinaryData |
Return values, #[returns(ty = ...)], type must be set, one of:
| Type definition | Rust type | 1C type |
|---|---|---|
Int |
i32 |
Number |
Float |
f64 |
Number |
Bool |
bool |
Boolean |
Str |
String |
String |
Date |
chrono::NaiveDateTime |
Date |
Blob |
Vec<u8> |
BinaryData |
None |
() |
Undefined |
Additionally, Result<T, ()> can be used, where T is one of the above. In this case, result
must be set in #[returns(...)] attribute: #[returns(Int, result)] for Result<i32, ()>
Example
# Cargo.toml
[package]
name = "my_addin"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
utf16_lit = "2.0"
native_api_1c = "0.10.5"
// src/lib.rs
use std::sync::Arc;
use native_api_1c::{
native_api_1c_core::ffi::connection::Connection,
native_api_1c_macro::{extern_functions, AddIn},
};
#[derive(AddIn)]
pub struct SampleAddIn {
/// connection with 1C, used for calling events
/// Arc is used to allow multiple threads to access the connection
#[add_in_con]
connection: Arc<Option<&'static Connection>>,
/// Property, readable and writable from 1C
#[add_in_prop(ty = Int, name = "MyProp", name_ru = "МоеСвойство", readable, writable)]
pub some_prop: i32,
/// Property, readable from 1C but not writable
#[add_in_prop(ty = Int, name = "ProtectedProp", name_ru = "ЗащищенноеСвойство", readable)]
pub protected_prop: i32,
/// Function, taking one or two arguments and returning a result
/// In 1C it can be called as:
/// ```bsl
/// CompObj.MyFunction(10, 15); // 2nd arg = 15
/// CompObj.MyFunction(10); // 2nd arg = 12 (default value)
/// ```
/// If function returns an error, but does not panic, then 1C will throw an exception
#[add_in_func(name = "MyFunction", name_ru = "МояФункция")]
#[arg(ty = Int)]
#[arg(ty = Int, default = 12)] // default value for the second argument
#[returns(ty = Int, result)]
pub my_function: fn(&Self, i32, i64) -> Result<i32, ()>,
/// Function, taking no arguments and returning nothing
#[add_in_func(name = "MyProcedure", name_ru = "МояПроцедура")]
pub my_procedure: fn(&mut Self),
/// Private field, not visible from 1C
private_field: i32,
}
impl Default for SampleAddIn {
fn default() -> Self {
Self {
connection: Arc::new(None),
some_prop: 0,
protected_prop: 50,
my_function: Self::my_function_inner,
my_procedure: Self::my_procedure_inner,
private_field: 100,
}
}
}
impl SampleAddIn {
fn my_function_inner(&self, arg: i32, arg_maybe_default: i64) -> Result<i32, ()> {
Ok(self.protected_prop
+ self.some_prop
+ arg
+ self.private_field
+ arg_maybe_default as i32)
}
fn my_procedure_inner(&mut self) {
self.protected_prop += 10;
}
}
extern_functions! {
SampleAddIn::default(),
}
Adding more objects
Method extern_functions! can take multiple objects, like this:
extern_functions! {
SampleAddIn::default(),
AnotherAddIn::default(),
YetAnotherAddIn::default(),
}
These object must have trait AddIn implemented. This can be done either with #[derive(AddIn)]
or manually. Latter is useful when you need some unusual behaviors that cannot be derived.