1
0

macro development iteration

This commit is contained in:
Maxim Kozlov
2023-08-08 17:25:50 +06:00
parent 7650f6cacb
commit 9764a2c01d
11 changed files with 57 additions and 664 deletions

View File

@ -1,11 +1,8 @@
[package]
name = "addin"
name = "native_api_1c"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
# from https://github.com/johnthagen/min-sized-rust
[profile.release]
opt-level = "z" # Optimize for size.
@ -15,9 +12,6 @@ panic = "abort" # Abort on panic
strip = true # Automatically strip symbols from the binary.
[dependencies]
base64 = "0.21.2"
color-eyre = "0.6.2"
log = "0.4.19"
log4rs = "1.2.0"
ureq = "2.7.1"
utf16_lit = "2.0"
syn = { version = "2.0.28", features = ["full"] }
quote = "1.0.32"

View File

@ -1,321 +0,0 @@
use crate::ffi::{
connection::Connection,
types::{ParamValue, ReturnValue},
utils::{from_os_string, os_string_nil},
};
use color_eyre::eyre::Result;
pub struct ComponentPropDescription {
pub names: &'static [&'static str; 2],
pub readable: bool,
pub writable: bool,
}
pub struct ComponentFuncDescription {
pub names: &'static [&'static str; 2],
pub params_count: usize,
pub returns_val: bool,
pub default_values: &'static [Option<ParamValue>],
}
impl ComponentFuncDescription {
pub fn new<const PARAMS_COUNT: usize>(
names: &'static [&'static str; 2],
returns_val: bool,
default_values: &'static [Option<ParamValue>; PARAMS_COUNT],
) -> Self {
Self {
names,
params_count: PARAMS_COUNT,
returns_val,
default_values,
}
}
}
pub trait AddIn {
fn init(&mut self, interface: &'static Connection) -> bool;
fn add_in_name(&self) -> &'static [u16];
fn list_parameters(&self) -> Vec<&ComponentPropDescription> {
Vec::new()
}
fn list_functions(&self) -> Vec<&ComponentFuncDescription> {
Vec::new()
}
fn get_parameter(&self, _name: &str) -> Option<ParamValue> {
None
}
fn set_parameter(&mut self, _name: &str, _value: &ParamValue) -> bool {
false
}
fn call_function(
&mut self,
_name: &str,
_params: &[ParamValue],
) -> Result<Option<ParamValue>> {
Ok(None)
}
}
pub struct AddInContainer<T: AddIn> {
add_in: T,
}
impl<T: AddIn> AddInContainer<T> {
pub fn new(add_in: T) -> Self {
Self { add_in }
}
}
impl<T: AddIn> AddInWrapper for AddInContainer<T> {
fn init(&mut self, interface: &'static Connection) -> bool {
self.add_in.init(interface)
}
/// default 2000, don't use version 1000, because static objects are created
fn get_info(&self) -> u16 {
2000
}
fn done(&mut self) {}
fn register_extension_as(&mut self) -> &'static [u16] {
self.add_in.add_in_name()
}
fn get_n_props(&self) -> usize {
self.add_in.list_parameters().len()
}
fn find_prop(&self, name: &[u16]) -> Option<usize> {
let name_str = from_os_string(name);
self.add_in
.list_parameters()
.iter()
.position(|x| x.names.iter().any(|y| y == &name_str))
}
fn get_prop_name(&self, num: usize, alias: usize) -> Option<Vec<u16>> {
self.add_in
.list_parameters()
.get(num)
.map(|x| os_string_nil(x.names[alias]))
}
fn get_prop_val(&self, num: usize, val: ReturnValue) -> bool {
let param_desc_binding = self.add_in.list_parameters();
let Some(param_desc) = param_desc_binding.get(num) else {
return false
};
if !param_desc.readable {
return false;
};
let Some(param_data) = self.add_in.get_parameter(param_desc.names[0]) else {
return false
};
match param_data {
ParamValue::I32(data_unwrapped) => val.set_i32(data_unwrapped),
ParamValue::Bool(data_unwrapped) => val.set_bool(data_unwrapped),
ParamValue::F64(data_unwrapped) => val.set_f64(data_unwrapped),
ParamValue::Date(data_unwrapped) => val.set_date(data_unwrapped),
ParamValue::Str(data_unwrapped) => val.set_str(&data_unwrapped),
ParamValue::Blob(data_unwrapped) => val.set_blob(&data_unwrapped),
ParamValue::Empty => val.set_empty(),
}
true
}
fn set_prop_val(&mut self, num: usize, val: &ParamValue) -> bool {
let param_desc_binding = self.add_in.list_parameters();
let Some(param_desc) = param_desc_binding.get(num) else {
return false
};
if !param_desc.writable {
return false;
};
self.add_in.set_parameter(param_desc.names[0], val)
}
fn is_prop_readable(&self, num: usize) -> bool {
let param_desc_binding = self.add_in.list_parameters();
let Some(param_desc) = param_desc_binding.get(num) else {
return false
};
param_desc.readable
}
fn is_prop_writable(&self, num: usize) -> bool {
let param_desc_binding = self.add_in.list_parameters();
let Some(param_desc) = param_desc_binding.get(num) else {
return false
};
param_desc.writable
}
fn get_n_methods(&self) -> usize {
self.add_in.list_functions().len()
}
fn find_method(&self, name: &[u16]) -> Option<usize> {
let name_str = from_os_string(name);
self.add_in
.list_functions()
.iter()
.position(|x| x.names.iter().any(|y| y == &name_str))
}
fn get_method_name(&self, num: usize, alias: usize) -> Option<Vec<u16>> {
self.add_in
.list_functions()
.get(num)
.map(|x| os_string_nil(x.names[alias]))
}
fn get_n_params(&self, num: usize) -> usize {
let binding = self.add_in.list_functions();
let Some(func_desc) = binding.get(num) else {
return 0
};
func_desc.params_count
}
fn get_param_def_value(
&self,
method_num: usize,
param_num: usize,
value: ReturnValue,
) -> bool {
let func_desc_binding = self.add_in.list_functions();
let Some(func_desc) = func_desc_binding.get(method_num) else {
return false
};
let Some(default_value_option) = func_desc.default_values.get(param_num) else {
return false
};
let Some(default_value) = default_value_option else {
return false
};
match default_value {
ParamValue::Bool(data) => value.set_bool(*data),
ParamValue::I32(data) => value.set_i32(*data),
ParamValue::F64(data) => value.set_f64(*data),
ParamValue::Date(data) => value.set_date(*data),
ParamValue::Str(data) => value.set_str(data),
ParamValue::Blob(data) => value.set_blob(data),
ParamValue::Empty => value.set_empty(),
}
true
}
fn has_ret_val(&self, method_num: usize) -> bool {
let func_desc_binding = self.add_in.list_functions();
let Some(func_desc) = func_desc_binding.get(method_num) else {
return false
};
func_desc.returns_val
}
fn call_as_proc(
&mut self,
method_num: usize,
params: &[ParamValue],
) -> bool {
let func_desc_binding = self.add_in.list_functions();
let Some(func_desc) = func_desc_binding.get(method_num) else {
return false
};
let _call_result =
match self.add_in.call_function(func_desc.names[0], params) {
Ok(r) => r,
Err(err) => {
log::error!("Error: {}", err);
return false;
}
};
true
}
fn call_as_func(
&mut self,
method_num: usize,
params: &[ParamValue],
val: ReturnValue,
) -> bool {
let func_desc_binding = self.add_in.list_functions();
let Some(func_desc) = func_desc_binding.get(method_num) else {
return false
};
if !func_desc.returns_val {
return false;
}
let call_result =
match self.add_in.call_function(func_desc.names[0], params) {
Ok(r) => r,
Err(err) => {
log::error!("Error: {}", err);
return false;
}
};
let Some(return_value) = call_result else {return false};
match return_value {
ParamValue::Bool(data) => val.set_bool(data),
ParamValue::I32(data) => val.set_i32(data),
ParamValue::F64(data) => val.set_f64(data),
ParamValue::Date(data) => val.set_date(data),
ParamValue::Str(data) => val.set_str(&data),
ParamValue::Blob(data) => val.set_blob(&data),
ParamValue::Empty => val.set_empty(),
}
true
}
fn set_locale(&mut self, _loc: &[u16]) {}
fn set_user_interface_language_code(&mut self, _lang: &[u16]) {}
}
pub trait AddInWrapper {
fn init(&mut self, interface: &'static Connection) -> bool;
/// default 2000, don't use version 1000, because static objects are created
fn get_info(&self) -> u16 {
2000
}
fn done(&mut self);
fn register_extension_as(&mut self) -> &[u16];
fn get_n_props(&self) -> usize;
fn find_prop(&self, name: &[u16]) -> Option<usize>;
fn get_prop_name(&self, num: usize, alias: usize) -> Option<Vec<u16>>;
fn get_prop_val(&self, num: usize, val: ReturnValue) -> bool;
fn set_prop_val(&mut self, num: usize, val: &ParamValue) -> bool;
fn is_prop_readable(&self, num: usize) -> bool;
fn is_prop_writable(&self, num: usize) -> bool;
fn get_n_methods(&self) -> usize;
fn find_method(&self, name: &[u16]) -> Option<usize>;
fn get_method_name(&self, num: usize, alias: usize) -> Option<Vec<u16>>;
fn get_n_params(&self, num: usize) -> usize;
fn get_param_def_value(
&self,
method_num: usize,
param_num: usize,
value: ReturnValue,
) -> bool;
fn has_ret_val(&self, method_num: usize) -> bool;
fn call_as_proc(
&mut self,
method_num: usize,
params: &[ParamValue],
) -> bool;
fn call_as_func(
&mut self,
method_num: usize,
params: &[ParamValue],
val: ReturnValue,
) -> bool;
fn set_locale(&mut self, loc: &[u16]);
fn set_user_interface_language_code(&mut self, lang: &[u16]);
}

View File

@ -4,11 +4,7 @@ use std::{
};
use utf16_lit::utf16_null;
use crate::{
add_in::AddInContainer,
ffi::{create_component, destroy_component, AttachType},
init_my_add_in,
};
use crate::ffi::{destroy_component, AttachType};
pub static mut PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1);
@ -18,13 +14,7 @@ pub unsafe extern "C" fn GetClassObject(
name: *const u16,
component: *mut *mut c_void,
) -> c_long {
match *name as u8 {
b'1' => {
let my_add_in_container = AddInContainer::new(init_my_add_in());
create_component(component, my_add_in_container)
}
_ => 0,
}
0
}
#[allow(non_snake_case)]

View File

@ -1,7 +1,7 @@
use std::ffi::c_long;
use super::{connection::Connection, memory_manager::MemoryManager, This};
use crate::add_in::AddInWrapper;
use crate::interface::AddInWrapper;
#[repr(C)]
pub struct InitDoneBaseVTable<T: AddInWrapper> {

View File

@ -4,7 +4,7 @@ use std::{
slice::from_raw_parts,
};
use crate::add_in::AddInWrapper;
use crate::interface::AddInWrapper;
use super::{
get_str,

View File

@ -3,7 +3,7 @@ use std::{
ptr,
};
use crate::add_in::AddInWrapper;
use crate::interface::AddInWrapper;
use self::{
init_base::InitDoneBaseVTable, lang_extender::LanguageExtenderBaseVTable,

46
src/interface.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::ffi::{
connection::Connection,
types::{ParamValue, ReturnValue},
};
pub trait AddInWrapper {
fn init(&mut self, interface: &'static Connection) -> bool;
/// default 2000, don't use version 1000, because static objects are created
fn get_info(&self) -> u16 {
2000
}
fn done(&mut self);
fn register_extension_as(&mut self) -> &[u16];
fn get_n_props(&self) -> usize;
fn find_prop(&self, name: &[u16]) -> Option<usize>;
fn get_prop_name(&self, num: usize, alias: usize) -> Option<Vec<u16>>;
fn get_prop_val(&self, num: usize, val: ReturnValue) -> bool;
fn set_prop_val(&mut self, num: usize, val: &ParamValue) -> bool;
fn is_prop_readable(&self, num: usize) -> bool;
fn is_prop_writable(&self, num: usize) -> bool;
fn get_n_methods(&self) -> usize;
fn find_method(&self, name: &[u16]) -> Option<usize>;
fn get_method_name(&self, num: usize, alias: usize) -> Option<Vec<u16>>;
fn get_n_params(&self, num: usize) -> usize;
fn get_param_def_value(
&self,
method_num: usize,
param_num: usize,
value: ReturnValue,
) -> bool;
fn has_ret_val(&self, method_num: usize) -> bool;
fn call_as_proc(
&mut self,
method_num: usize,
params: &[ParamValue],
) -> bool;
fn call_as_func(
&mut self,
method_num: usize,
params: &[ParamValue],
val: ReturnValue,
) -> bool;
fn set_locale(&mut self, loc: &[u16]);
fn set_user_interface_language_code(&mut self, lang: &[u16]);
}

View File

@ -1,11 +1,3 @@
pub mod add_in;
mod export_functions;
mod ffi;
mod my_add_in;
use add_in::AddIn;
use my_add_in::MyAddInDescription;
pub fn init_my_add_in() -> impl AddIn {
MyAddInDescription::new()
}
pub mod export_functions;
pub mod ffi;
pub mod interface;

View File

@ -1,181 +0,0 @@
use super::MyAddInDescription;
use crate::add_in::ComponentFuncDescription;
use crate::ffi::connection::Error;
use crate::ffi::utils::os_string;
use crate::ffi::{types::ParamValue, utils::from_os_string};
use base64::{engine::general_purpose, Engine as _};
use color_eyre::eyre::{eyre, Result};
use log::LevelFilter;
use log4rs::{
append::file::FileAppender,
config::{Appender, Root},
encode::pattern::PatternEncoder,
Config,
};
use std::{path::PathBuf, thread, time::Duration};
pub struct FunctionListElement {
pub description: ComponentFuncDescription,
pub callback: fn(
&mut MyAddInDescription,
&[ParamValue],
) -> Result<Option<ParamValue>>,
}
impl MyAddInDescription {
pub fn generate_func_list() -> Vec<FunctionListElement> {
vec![
FunctionListElement {
description: ComponentFuncDescription::new::<0>(
&["Итерировать", "Iterate"],
false,
&[],
),
callback: Self::iterate,
},
FunctionListElement {
description: ComponentFuncDescription::new::<1>(
&["Таймер", "Timer"],
true,
&[Some(ParamValue::I32(1000))],
),
callback: Self::timer,
},
FunctionListElement {
description: ComponentFuncDescription::new::<0>(
&["ПолучитьХэТэТэПэ", "FetchHTTP"],
true,
&[],
),
callback: Self::fetch,
},
FunctionListElement {
description: ComponentFuncDescription::new::<1>(
&["ПолучитьХэТэТэПэПараллельно", "FetchHTTPConcurrently"],
true,
&[None],
),
callback: Self::concurrent_fetch,
},
FunctionListElement {
description: ComponentFuncDescription::new::<1>(
&[("ИнициализироватьЛоггер"), ("InitLogger")],
false,
&[None],
),
callback: Self::init_logger,
},
]
}
fn iterate(
&mut self,
_params: &[ParamValue],
) -> Result<Option<ParamValue>> {
if self.some_prop_container >= 105 {
return Err(eyre!("Prop is too big"));
}
self.some_prop_container += 1;
log::info!("Prop is now {}", self.some_prop_container);
Ok(None)
}
fn timer(&mut self, params: &[ParamValue]) -> Result<Option<ParamValue>> {
let sleep_duration_ms = match params.get(0) {
Some(ParamValue::I32(val)) => *val,
_ => return Err(eyre!("Invalid parameter")),
};
if sleep_duration_ms < 0 {
return Err(eyre!("Invalid parameter"));
}
if sleep_duration_ms > 100000 {
return Err(eyre!("Too long"));
}
let sleep_duration_ms = sleep_duration_ms as u64;
let connection = self.connection.clone();
let name = from_os_string(self.name);
thread::spawn(move || {
log::info!("Timer started");
thread::sleep(Duration::from_millis(sleep_duration_ms));
log::info!("Timer ended");
if let Some(connection) = &*connection {
connection.external_event(&name, "TimerEnd", "OK");
connection.add_error(Error::DialogInfo, "Timer", "Time's up");
}
});
Ok(Some(ParamValue::I32(sleep_duration_ms as i32)))
}
fn fetch(&mut self, _params: &[ParamValue]) -> Result<Option<ParamValue>> {
let Ok(result) = ureq::post("https://echo.hoppscotch.io").send_string("smth") else {return Err(eyre!("Failed to fetch"));};
let Ok(body) = result.into_string() else { return Err(eyre!("Failed to get body"));};
Ok(Some(ParamValue::Str(os_string(&body))))
}
fn concurrent_fetch(
&mut self,
params: &[ParamValue],
) -> Result<Option<ParamValue>> {
let request_count = match params.get(0) {
Some(ParamValue::I32(val)) => *val,
_ => return Err(eyre!("Invalid parameter")),
};
if request_count < 0 {
return Err(eyre!("Invalid parameter"));
}
let request_count = request_count as u64;
let mut threads_handles = Vec::new();
for _ in 0..request_count {
let handle = thread::spawn(move || {
let Ok(result) = ureq::post("https://echo.hoppscotch.io").send_string("smth") else {return Err(eyre!("Failed to fetch"));};
let Ok(body) = result.into_string() else { return Err(eyre!("Failed to get body"));};
Ok(body)
});
threads_handles.push(handle);
}
let results: Vec<String> = threads_handles
.into_iter()
.map(|h| -> String {
let Ok(req_result) =
h.join() else { return format!("ERR")};
let Ok(req_text) = req_result else { return format!("ERR")};
let base64_text =
general_purpose::STANDARD_NO_PAD.encode(req_text);
base64_text
})
.collect();
Ok(Some(ParamValue::Str(os_string(&results.join(";")))))
}
fn init_logger(
&mut self,
params: &[ParamValue],
) -> Result<Option<ParamValue>> {
let log_file_path = match params.get(0) {
Some(ParamValue::Str(val)) => from_os_string(val),
_ => return Err(eyre!("Invalid parameter")),
};
let log_file_path = PathBuf::from(log_file_path);
if log_file_path.is_dir() {
return Err(eyre!("Need a file path"));
};
let log_file_appender = FileAppender::builder()
.encoder(Box::new(PatternEncoder::new("{d} - {m}{n}")))
.build(log_file_path)?;
let config = Config::builder()
.appender(
Appender::builder().build("file", Box::new(log_file_appender)),
)
.build(Root::builder().appender("file").build(LevelFilter::Info))?;
self.log_handle = Some(log4rs::init_config(config)?);
log::info!("Logger initialized");
Ok(None)
}
}

View File

@ -1,90 +0,0 @@
mod func;
mod props;
use crate::add_in::{
AddIn, ComponentFuncDescription, ComponentPropDescription,
};
use crate::ffi::{connection::Connection, types::ParamValue};
use color_eyre::eyre::{eyre, Result};
use std::sync::Arc;
use utf16_lit::utf16_null;
use self::func::FunctionListElement;
use self::props::PropListElement;
pub struct MyAddInDescription {
name: &'static [u16],
connection: Arc<Option<&'static Connection>>,
log_handle: Option<log4rs::Handle>,
functions: Vec<FunctionListElement>,
props: Vec<PropListElement>,
some_prop_container: i32,
}
impl MyAddInDescription {
pub fn new() -> Self {
Self {
name: &utf16_null!("MyAddIn"),
connection: Arc::new(None),
log_handle: None,
functions: Self::generate_func_list(),
props: Self::generate_prop_list(),
some_prop_container: 0,
}
}
}
impl AddIn for MyAddInDescription {
fn init(&mut self, interface: &'static Connection) -> bool {
interface.set_event_buffer_depth(10);
self.connection = Arc::new(Some(interface));
self.some_prop_container = 100;
true
}
fn add_in_name(&self) -> &'static [u16] {
self.name
}
fn call_function(
&mut self,
name: &str,
params: &[ParamValue],
) -> Result<Option<ParamValue>> {
let func = self
.functions
.iter()
.find(|el| el.description.names.iter().any(|n| n == &name));
let Some(func) = func.map(|el| el.callback) else { return Err(eyre!("No function with such name")) };
func(self, params)
}
fn get_parameter(&self, name: &str) -> Option<ParamValue> {
let prop = self
.props
.iter()
.find(|el| el.description.names.iter().any(|n| n == &name));
let Some(Some(get)) = prop.map(|el| el.get_callback) else { return None };
get(self)
}
fn set_parameter(&mut self, name: &str, value: &ParamValue) -> bool {
let prop = self
.props
.iter()
.find(|el| el.description.names.iter().any(|n| n == &name));
let Some(Some(set)) = prop.map(|el| el.set_callback) else { return false };
set(self, value)
}
fn list_functions(&self) -> Vec<&ComponentFuncDescription> {
self.functions.iter().map(|el| &el.description).collect()
}
fn list_parameters(&self) -> Vec<&ComponentPropDescription> {
self.props.iter().map(|el| &el.description).collect()
}
}

View File

@ -1,37 +0,0 @@
use crate::{add_in::ComponentPropDescription, ffi::types::ParamValue};
use super::MyAddInDescription;
pub struct PropListElement {
pub description: ComponentPropDescription,
pub get_callback: Option<fn(&MyAddInDescription) -> Option<ParamValue>>,
pub set_callback: Option<fn(&mut MyAddInDescription, &ParamValue) -> bool>,
}
impl MyAddInDescription {
pub fn generate_prop_list() -> Vec<PropListElement> {
vec![PropListElement {
description: ComponentPropDescription {
names: &["Property", "Свойство"],
readable: true,
writable: true,
},
get_callback: Some(Self::get_prop),
set_callback: Some(Self::set_prop),
}]
}
fn get_prop(&self) -> Option<ParamValue> {
Some(ParamValue::I32(self.some_prop_container))
}
fn set_prop(&mut self, value: &ParamValue) -> bool {
match value {
ParamValue::I32(val) => {
self.some_prop_container = *val;
true
}
_ => false,
}
}
}