feat: add abi stable crates
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,6 +9,8 @@ linux_target/
|
||||
node_modules/
|
||||
*.o
|
||||
*.so
|
||||
pluginlibs
|
||||
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
5
__ffi/abi_stable_crates/README.md
Normal file
5
__ffi/abi_stable_crates/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
url
|
||||
https://github.com/rodrimati1992/abi_stable_crates/tree/master/examples/1_trait_objects
|
||||
|
||||
367
__ffi/abi_stable_crates/application/Cargo.lock
generated
Normal file
367
__ffi/abi_stable_crates/application/Cargo.lock
generated
Normal file
@@ -0,0 +1,367 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "abi_stable"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112fbc83f18cf0f12358975cac27eb4556cc5d777bde72312e0cec7a294e1699"
|
||||
dependencies = [
|
||||
"abi_stable_derive",
|
||||
"abi_stable_shared",
|
||||
"core_extensions",
|
||||
"crossbeam-channel",
|
||||
"generational-arena",
|
||||
"libloading",
|
||||
"lock_api",
|
||||
"parking_lot",
|
||||
"repr_offset",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abi_stable_derive"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1be19f4da4a4a1f2537f1f14d8f0917c07758acb7419d3bad9e9d62a11f6b5b0"
|
||||
dependencies = [
|
||||
"abi_stable_shared",
|
||||
"as_derive_utils",
|
||||
"core_extensions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"typed-arena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abi_stable_shared"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96112d6a13f37dd1cc2a2f07f7f12b7c3d176b6eec36e7987432a70fece8b48"
|
||||
dependencies = [
|
||||
"core_extensions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "as_derive_utils"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc3613a62c7b739739a2cb1ee166ac275f16c5b86caf454ba21a2f79f04b025"
|
||||
dependencies = [
|
||||
"core_extensions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "core_extensions"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff55d62b113b98e0b0a55112741376c4cfe36f1290d18392c2c5727e09629a7f"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example_application"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"core_extensions",
|
||||
"example_interface",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example_interface"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"arrayvec",
|
||||
"core_extensions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generational-arena"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "repr_offset"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a68e33940e1f1e7ab77493ad7d983dba544ce72185c61d4b1b23e6c32dd2a165"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
13
__ffi/abi_stable_crates/application/Cargo.toml
Normal file
13
__ffi/abi_stable_crates/application/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "example_application"
|
||||
version = "0.1.0"
|
||||
authors = ["rodrimati1992 <rodrimatt1985@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
abi_stable = "0.9.1"
|
||||
core_extensions={ version = "0.1.18", default_features = false, features = ["std"] }
|
||||
serde={ version = "1.0.117", features = ["derive"] }
|
||||
serde_json = { version = "1.0.59", features = ["raw_value"] }
|
||||
smallvec= "1.4.2"
|
||||
example_interface={ version="0.1", path="../interface" }
|
||||
33
__ffi/abi_stable_crates/application/data/app_config.json
Normal file
33
__ffi/abi_stable_crates/application/data/app_config.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"plugins":[
|
||||
{
|
||||
"name":"example_plugin_text_munging",
|
||||
"instances":2,
|
||||
"rename":"text_munging"
|
||||
},
|
||||
{
|
||||
"name":"example_plugin_utils",
|
||||
"rename":"utils"
|
||||
}
|
||||
],
|
||||
"_hidden":[
|
||||
],
|
||||
"commands":{
|
||||
"text_munging:first":{"Rot13":"what"},
|
||||
"text_munging:last":"GetCommands",
|
||||
"text_munging:all":{"CapitalizeWords":{
|
||||
"text":"This is an example text.",
|
||||
"words":["an","text"]
|
||||
}},
|
||||
"utils":{"Batch":{
|
||||
"plugin":"text_munging:first",
|
||||
"commands":[
|
||||
{"Rot13":"Fancr xvyyf Qhzoyrqber"},
|
||||
{"CapitalizeWords":{
|
||||
"text":"Oh,that's not something I expected.",
|
||||
"words":["not","expected"]
|
||||
}}
|
||||
]
|
||||
}}
|
||||
}
|
||||
}
|
||||
206
__ffi/abi_stable_crates/application/src/app.rs
Normal file
206
__ffi/abi_stable_crates/application/src/app.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use super::*;
|
||||
use std::time::{Duration,Instant};
|
||||
|
||||
pub struct TheApplication{
|
||||
pub(super) plugins:Vec<PluginType>,
|
||||
pub(super) state:ApplicationState,
|
||||
}
|
||||
|
||||
|
||||
pub struct ApplicationState{
|
||||
// This is separate so that I can use the address
|
||||
// of a PluginType to find its index inside of plugins.
|
||||
pub(super) plugin_ids:Vec<PluginId>,
|
||||
pub(super) id_map:HashMap<RString,PluginModAndIndices>,
|
||||
pub(super) delayed_commands:VecDeque<DelayedCommand>,
|
||||
pub(super) responses:VecDeque<DelayedResponse>,
|
||||
pub(super) sender:RSender<AsyncCommand>,
|
||||
pub(super) receiver:RReceiver<AsyncCommand>,
|
||||
pub(super) last_run_at:Instant,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn print_response(
|
||||
plugin_id:&PluginId,
|
||||
response:&str,
|
||||
){
|
||||
println!(
|
||||
"reponse:\n{}\nfrom:\n {:?}\n\n",
|
||||
response.left_pad(4),
|
||||
plugin_id,
|
||||
);
|
||||
}
|
||||
|
||||
impl TheApplication{
|
||||
/// Runs a command,
|
||||
pub fn run_command(&mut self,which_plugin:WhichPlugin,command:RStr<'_>)->Result<(),AppError>{
|
||||
let list=self.state.expand_which_plugin(which_plugin)?;
|
||||
for index in list {
|
||||
let state=Application_TO::from_ptr(&mut self.state,TU_Opaque);
|
||||
let plugin=&mut self.plugins[index as usize];
|
||||
println!("command:\n{}", command.left_pad(4));
|
||||
let resp=plugin.json_command(command,state).into_result()?;
|
||||
self.state.register_command_run();
|
||||
print_response(&self.state.plugin_ids[index],&resp);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) -> Result<(), AppError>{
|
||||
if let Ok(ac)=self.state.receiver.try_recv() {
|
||||
self.state.send_command_to_plugin(&ac.from,ac.which_plugin,ac.command).into_result()?;
|
||||
}
|
||||
|
||||
if let Some(dc)=self.state.delayed_commands.pop_front() {
|
||||
self.run_command_(&dc.from,dc.plugin_index,&dc.command)?;
|
||||
}
|
||||
|
||||
let mut responses=mem::replace(&mut self.state.responses, VecDeque::new());
|
||||
for DelayedResponse{to,from,response} in responses.drain(..) {
|
||||
let response=PluginResponse::owned_response(from,response);
|
||||
let state=Application_TO::from_ptr(&mut self.state,TU_Opaque);
|
||||
if let RSome(res)=self.plugins[to]
|
||||
.handle_response(response, state)
|
||||
.into_result()?
|
||||
{
|
||||
print_response(&res.plugin_id, &res.response);
|
||||
}
|
||||
}
|
||||
self.state.responses=responses;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_finished(&self)->bool{
|
||||
self.state.last_run_at.elapsed()>=Duration::from_secs(5)
|
||||
}
|
||||
|
||||
fn run_command_(&mut self,from:&PluginId, to:usize, command:&str) -> Result<(), AppError> {
|
||||
let state=Application_TO::from_ptr(&mut self.state,TU_Opaque);
|
||||
let response=self.plugins[to].json_command(command.into(),state).into_result()?;
|
||||
let to=self.state.index_for_plugin_id(from)?;
|
||||
|
||||
self.state.register_command_run();
|
||||
|
||||
let response = DelayedResponse {
|
||||
from:self.state.plugin_ids[to].clone(),
|
||||
to,
|
||||
response,
|
||||
};
|
||||
|
||||
self.state.responses.push_back(response);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ApplicationState{
|
||||
pub(crate) fn new() -> Self {
|
||||
let (sender,receiver)=crossbeam_channel::unbounded();
|
||||
|
||||
Self{
|
||||
plugin_ids:Vec::new(),
|
||||
id_map:HashMap::new(),
|
||||
delayed_commands:VecDeque::new(),
|
||||
responses:VecDeque::new(),
|
||||
sender,
|
||||
receiver,
|
||||
last_run_at:Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_command_run(&mut self) {
|
||||
self.last_run_at=Instant::now();
|
||||
}
|
||||
|
||||
fn index_for_plugin_id(&self, id:&PluginId) -> Result<usize,AppError> {
|
||||
self.id_map.get(&*id.named)
|
||||
.and_then(|x| x.indices.get(id.instance as usize).cloned())
|
||||
.ok_or_else(|| AppError::InvalidPlugin(WhichPlugin::Id(id.clone())))
|
||||
}
|
||||
|
||||
fn expand_which_plugin(&self, which_plugin:WhichPlugin) -> Result<PluginIndices, AppError> {
|
||||
match which_plugin {
|
||||
WhichPlugin::Id(id)=>{
|
||||
self.index_for_plugin_id(&id)
|
||||
.map(|i| PluginIndices::from([i]) )
|
||||
},
|
||||
WhichPlugin::First{ref named}
|
||||
| WhichPlugin::Last{ref named}
|
||||
| WhichPlugin::Every{ref named} => {
|
||||
self.id_map.get(&**named).and_then(|x| {
|
||||
let list=&x.indices;
|
||||
match which_plugin {
|
||||
WhichPlugin::First{..} => {
|
||||
PluginIndices::from([*list.first()?])
|
||||
},
|
||||
WhichPlugin::Last{..} => {
|
||||
PluginIndices::from([*list.last()?])
|
||||
},
|
||||
WhichPlugin::Every{..} => {
|
||||
PluginIndices::from(&**list)
|
||||
},
|
||||
_=>unreachable!(),
|
||||
}.piped(Some)
|
||||
})
|
||||
.ok_or_else(|| AppError::InvalidPlugin(which_plugin.clone()))
|
||||
},
|
||||
WhichPlugin::Many(list) => {
|
||||
let mut plugin_indices=PluginIndices::new();
|
||||
for elem in list {
|
||||
plugin_indices.extend(self.expand_which_plugin(elem)?);
|
||||
}
|
||||
Ok(plugin_indices)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Application for ApplicationState {
|
||||
fn send_command_to_plugin(
|
||||
&mut self,
|
||||
from:&PluginId,
|
||||
which_plugin:WhichPlugin,
|
||||
command:RString,
|
||||
) -> RResult<(),AppError> {
|
||||
self.expand_which_plugin(which_plugin).map(|plugin_indices|{
|
||||
let command=Arc::new(command);
|
||||
for plugin_index in plugin_indices {
|
||||
let from=from.clone();
|
||||
self.delayed_commands.push_back(DelayedCommand {
|
||||
from,
|
||||
plugin_index,
|
||||
command:command.clone(),
|
||||
});
|
||||
}
|
||||
}).into()
|
||||
}
|
||||
|
||||
fn get_plugin_id(&self, which_plugin:WhichPlugin) -> RResult<RVec<PluginId>, AppError> {
|
||||
self.expand_which_plugin(which_plugin)
|
||||
.map(|list|{
|
||||
list.into_iter()
|
||||
.map(|i| self.plugin_ids[i].clone() )
|
||||
.collect::<RVec<PluginId>>()
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
fn sender(&self) -> RSender<AsyncCommand> {
|
||||
self.sender.clone()
|
||||
}
|
||||
|
||||
fn loaded_plugins(&self) -> RVec<PluginId> {
|
||||
self.plugin_ids.clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
315
__ffi/abi_stable_crates/application/src/main.rs
Normal file
315
__ffi/abi_stable_crates/application/src/main.rs
Normal file
@@ -0,0 +1,315 @@
|
||||
use std::{collections::{HashMap, VecDeque}, io, path::{Path, PathBuf}, mem, sync::Arc};
|
||||
use abi_stable::{
|
||||
external_types::crossbeam_channel::{self, RSender, RReceiver},
|
||||
std_types::{RString, RStr, RResult, RVec, ROk, RErr, RSome},
|
||||
sabi_trait::prelude::TU_Opaque,
|
||||
library::{RawLibrary, LibraryError, LibrarySuffix, lib_header_from_path},
|
||||
};
|
||||
#[allow(unused_imports)]
|
||||
use core_extensions::{SelfOps, SliceExt, StringExt};
|
||||
use example_interface::{
|
||||
AsyncCommand,
|
||||
Application_TO,
|
||||
Application,
|
||||
Error as AppError,
|
||||
PluginId,
|
||||
PluginMod_Ref,
|
||||
PluginType,
|
||||
PluginResponse,
|
||||
WhichPlugin,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::value::RawValue;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
mod vec_from_map;
|
||||
mod app;
|
||||
|
||||
use crate::{
|
||||
app::{TheApplication,ApplicationState},
|
||||
vec_from_map::VecFromMap,
|
||||
};
|
||||
|
||||
|
||||
/// Returns the path the plugin will be loaded from.
|
||||
fn compute_plugin_path(base_name:&str)->io::Result<PathBuf>{
|
||||
let debug_dir = "pluginlibs/".as_ref_::<Path>().into_(PathBuf::T);
|
||||
let release_dir = "pluginlibs/".as_ref_::<Path>().into_(PathBuf::T);
|
||||
|
||||
let debug_path=
|
||||
RawLibrary::path_in_directory(&debug_dir ,base_name,LibrarySuffix::NoSuffix);
|
||||
|
||||
let release_path=
|
||||
RawLibrary::path_in_directory(&release_dir,base_name,LibrarySuffix::NoSuffix);
|
||||
|
||||
match (debug_path.exists(),release_path.exists()) {
|
||||
(false,false)=>debug_path,
|
||||
(true,false)=>debug_path,
|
||||
(false,true)=>release_path,
|
||||
(true,true)=>{
|
||||
if debug_path.metadata()?.modified()? < release_path.metadata()?.modified()? {
|
||||
release_path
|
||||
}else{
|
||||
debug_path
|
||||
}
|
||||
}
|
||||
}.piped(Ok)
|
||||
}
|
||||
|
||||
/// A description of what plugin to load.
|
||||
#[derive(Debug,Clone,Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum PluginToLoad{
|
||||
Named(String),
|
||||
WithInstances{
|
||||
#[serde(alias = "name")]
|
||||
named:String,
|
||||
#[serde(default="one_u64")]
|
||||
instances:u64,
|
||||
#[serde(alias = "renamed")]
|
||||
rename:Option<String>,
|
||||
}
|
||||
}
|
||||
|
||||
fn one_u64()->u64{
|
||||
1
|
||||
}
|
||||
|
||||
/// The type that the configuration file is deserialized into.
|
||||
#[derive(Debug,Clone,Deserialize)]
|
||||
pub struct Config{
|
||||
pub plugins:RVec<PluginToLoad>,
|
||||
pub commands:VecFromMap<WhichPlugin,Box<RawValue>>,
|
||||
}
|
||||
|
||||
|
||||
/// A description of plugin instances that were instantiated and left to instantiate,
|
||||
/// as well as the root module of the plugin's dynamic library to instantiate the plugins.
|
||||
pub struct PluginModAndIndices{
|
||||
root_module:PluginMod_Ref,
|
||||
to_be_instantiated:u64,
|
||||
indices:Vec<usize>,
|
||||
}
|
||||
|
||||
|
||||
pub type PluginIndices=SmallVec<[usize;1]>;
|
||||
|
||||
/// Commands sent to plugins after calling `Application::send_command_to_plugin`.
|
||||
#[derive(Debug)]
|
||||
pub struct DelayedCommand{
|
||||
/// Which plugin sent the command
|
||||
from:PluginId,
|
||||
/// The index in plugins to which the command is sent.
|
||||
plugin_index:usize,
|
||||
/// The command for the `plugin_index` plugin.
|
||||
command:Arc<RString>,
|
||||
}
|
||||
|
||||
|
||||
/// Used to handle the responses to the delayed commands sent to plugins after calling
|
||||
/// `Application::send_command_to_plugin`.
|
||||
#[derive(Debug)]
|
||||
pub struct DelayedResponse{
|
||||
/// The plugin that sends the reponse.
|
||||
from:PluginId,
|
||||
/// The plugin that sent the command for which this is the reponse.
|
||||
to:usize,
|
||||
/// The response from the `from` plugin to the `to` plugin.
|
||||
response:RString,
|
||||
}
|
||||
|
||||
|
||||
fn main()-> io::Result<()> {
|
||||
|
||||
let config_path = match std::env::args_os().nth(1) {
|
||||
Some(os)=>PathBuf::from(os),
|
||||
None=>{
|
||||
println!(
|
||||
"Help:You can pass a configuration's path as a command-line argument."
|
||||
);
|
||||
PathBuf::from("./data/app_config.json")
|
||||
}
|
||||
};
|
||||
|
||||
let file_contents=match std::fs::read_to_string(&*config_path) {
|
||||
Ok(x)=>x,
|
||||
Err(e)=>{
|
||||
eprintln!(
|
||||
"Could not load the configuration file at:\n\
|
||||
\t{}\n\
|
||||
Because of this error:\n{}\n",
|
||||
config_path.display(),
|
||||
e
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let config:Config=match serde_json::from_str(&file_contents) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"Could not parse the configuration file at:\n\
|
||||
\t{}\n\
|
||||
Because of this error:\n\
|
||||
{}\n",
|
||||
config_path.display(),
|
||||
e
|
||||
);
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
|
||||
let mut nonexistent_files=Vec::<(String,io::Error)>::new();
|
||||
|
||||
let mut library_errs=Vec::<(String,LibraryError)>::new();
|
||||
|
||||
let mut loaded_libraries=Vec::<String>::new();
|
||||
|
||||
let mut plugins=Vec::new();
|
||||
let mut state=ApplicationState::new();
|
||||
|
||||
for plug in &config.plugins {
|
||||
let (named, instances, rename) = match plug {
|
||||
PluginToLoad::Named(named) => ((*named).clone(),1,None),
|
||||
PluginToLoad::WithInstances{named,instances,rename} =>
|
||||
((*named).clone(),*instances,rename.clone()),
|
||||
};
|
||||
|
||||
let name_key=rename.unwrap_or_else(||named.clone());
|
||||
|
||||
if let Some(mod_i)=state.id_map.get_mut(&*name_key) {
|
||||
mod_i.to_be_instantiated+=instances;
|
||||
continue;
|
||||
}
|
||||
|
||||
let library_path:PathBuf = match compute_plugin_path(named.as_ref()) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
nonexistent_files.push((named.clone(),e));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let res = (|| {
|
||||
let header=lib_header_from_path(&library_path)?;
|
||||
header.init_root_module::<PluginMod_Ref>()
|
||||
})();
|
||||
|
||||
let root_module=match res {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
library_errs.push((named.clone(),e));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
loaded_libraries.push(name_key.clone());
|
||||
|
||||
state.id_map.insert(name_key.into_(RString::T),PluginModAndIndices{
|
||||
root_module,
|
||||
to_be_instantiated:instances,
|
||||
indices:Vec::with_capacity(instances as usize),
|
||||
});
|
||||
}
|
||||
|
||||
if !nonexistent_files.is_empty(){
|
||||
for (name, e) in nonexistent_files {
|
||||
eprintln!(
|
||||
"Could not load librarr:\n\
|
||||
\t{}\n\
|
||||
because of this error:\n\
|
||||
{}\n\
|
||||
",
|
||||
name,e
|
||||
)
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if !library_errs.is_empty(){
|
||||
for (name,e) in library_errs {
|
||||
eprintln!(
|
||||
"Could not load librarr:\n\
|
||||
\t{}\n\
|
||||
because of this error:\n\
|
||||
{}\n\
|
||||
",
|
||||
name,e
|
||||
)
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let mut plugin_new_errs=Vec::<(String,AppError)>::new();
|
||||
|
||||
for name in loaded_libraries {
|
||||
let mod_i=state.id_map.get_mut(&*name).unwrap();
|
||||
for _ in 0..mem::replace(&mut mod_i.to_be_instantiated,0) {
|
||||
let plugin_constructor=mod_i.root_module.new();
|
||||
let new_id=PluginId{
|
||||
named:name.clone().into(),
|
||||
instance:mod_i.indices.len() as u64,
|
||||
};
|
||||
let plugin=match plugin_constructor(state.sender.clone(),new_id.clone()) {
|
||||
ROk(x)=>x,
|
||||
RErr(e)=>{
|
||||
plugin_new_errs.push((name.clone(),e));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let new_index=plugins.len();
|
||||
|
||||
plugins.push(plugin);
|
||||
|
||||
mod_i.indices.push(new_index);
|
||||
state.plugin_ids.push(new_id);
|
||||
}
|
||||
}
|
||||
|
||||
if !plugin_new_errs.is_empty() {
|
||||
for (name,e) in plugin_new_errs {
|
||||
eprintln!(
|
||||
"Could not instantiate plugin:\n\
|
||||
\t{}\n\
|
||||
because of this error:\n\
|
||||
{}\n\
|
||||
",
|
||||
name,e
|
||||
)
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let mut config_commands=config.commands.vec.into_iter();
|
||||
|
||||
let mut app=TheApplication{
|
||||
plugins,
|
||||
state,
|
||||
};
|
||||
|
||||
while !app.is_finished() {
|
||||
if let Some((which_plugin,command))=config_commands.next() {
|
||||
let command=command.get();
|
||||
if let Err(e)=app.run_command(which_plugin.clone(),command.into()){
|
||||
eprintln!(
|
||||
"Error while running command on:\n{:?}\nError:{}\nCommand:\n{:?}\n",
|
||||
which_plugin,e,command
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e)=app.tick() {
|
||||
eprintln!("Error in application loop:\n{}\n",e);
|
||||
}
|
||||
}
|
||||
|
||||
if app.is_finished() {
|
||||
println!("timeout waiting for events");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
57
__ffi/abi_stable_crates/application/src/vec_from_map.rs
Normal file
57
__ffi/abi_stable_crates/application/src/vec_from_map.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use std::{fmt, marker::PhantomData};
|
||||
use serde::de::{Deserialize, Deserializer, Visitor, MapAccess};
|
||||
|
||||
|
||||
/// Used to deserialize a json object to a list of key-value pairs
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct VecFromMap<K,V>{
|
||||
pub vec:Vec<(K,V)>,
|
||||
}
|
||||
|
||||
|
||||
struct VecFromMapVisitor<K, V> {
|
||||
marker: PhantomData<(K, V)>
|
||||
}
|
||||
|
||||
impl<K, V> VecFromMapVisitor<K, V> {
|
||||
const NEW:Self=Self{marker:PhantomData};
|
||||
}
|
||||
|
||||
impl<'de, K, V> Visitor<'de> for VecFromMapVisitor<K, V>
|
||||
where
|
||||
K: Deserialize<'de>,
|
||||
V: Deserialize<'de>,
|
||||
{
|
||||
type Value = VecFromMap<K, V>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let cap=access.size_hint().unwrap_or(0);
|
||||
let mut vec = Vec::<(K,V)>::with_capacity(cap);
|
||||
|
||||
while let Some(pair) = access.next_entry()? {
|
||||
vec.push(pair);
|
||||
}
|
||||
|
||||
Ok(VecFromMap{vec})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, K, V> Deserialize<'de> for VecFromMap<K, V>
|
||||
where
|
||||
K: Deserialize<'de>,
|
||||
V: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(VecFromMapVisitor::NEW)
|
||||
}
|
||||
}
|
||||
12
__ffi/abi_stable_crates/interface/Cargo.toml
Normal file
12
__ffi/abi_stable_crates/interface/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "example_interface"
|
||||
version = "0.1.0"
|
||||
authors = ["rodrimati1992 <rodrimatt1985@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
abi_stable = "0.9.1"
|
||||
serde={ version = "1.0.117", features = ["derive"] }
|
||||
arrayvec= "0.5.1"
|
||||
core_extensions={ version = "0.1.18", default_features = false, features = ["std"] }
|
||||
serde_json= "1.0.59"
|
||||
154
__ffi/abi_stable_crates/interface/src/commands.rs
Normal file
154
__ffi/abi_stable_crates/interface/src/commands.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use crate::{PluginId,WhichPlugin};
|
||||
|
||||
use std::fmt;
|
||||
use serde::{
|
||||
Serialize,Deserialize,
|
||||
de::{self,Deserializer, DeserializeOwned, IgnoredAny, Visitor, MapAccess, Error as _},
|
||||
};
|
||||
use abi_stable::{StableAbi, std_types::*};
|
||||
|
||||
/// The commands that map to methods in the Plugin trait.
|
||||
// This is intentionally not `#[derive(StableAbi)]`,
|
||||
// since it can be extended in minor versions of the interface.
|
||||
// I has to be serialized to pass it through ffi.
|
||||
#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
|
||||
pub enum BasicCommand{
|
||||
GetCommands,
|
||||
}
|
||||
|
||||
|
||||
/// These is the (serialized) return value of calling `PluginExt::send_basic_command`.
|
||||
// This is intentionally not `#[derive(StableAbi)]`,
|
||||
// since it can be extended in minor versions of the interface.
|
||||
// I has to be serialized to pass it through ffi.
|
||||
#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
|
||||
pub enum BasicRetVal{
|
||||
GetCommands(RVec<CommandDescription>),
|
||||
}
|
||||
|
||||
|
||||
// This is intentionally not `#[derive(StableAbi)]`,
|
||||
// since it can be extended in minor versions of the interface.
|
||||
// I has to be serialized to pass it through ffi.
|
||||
#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CommandUnion<T>{
|
||||
ForPlugin(T),
|
||||
Basic(BasicCommand),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ReturnValUnion<T>{
|
||||
ForPlugin(T),
|
||||
Basic(BasicRetVal),
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// A partially deserialize command,that only deserialized its variant.
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct WhichVariant{
|
||||
pub variant:RString,
|
||||
}
|
||||
|
||||
|
||||
struct WhichVariantVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for WhichVariantVisitor{
|
||||
type Value = WhichVariant;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map with a single entry,or a string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<WhichVariant, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(WhichVariant{variant:value.to_string().into()})
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let (variant,_)=access.next_entry::<RString,IgnoredAny>()?
|
||||
.ok_or_else(||M::Error::custom("Expected a map with a single entry"))?;
|
||||
if let Some((second,_))=access.next_entry::<RString,IgnoredAny>()? {
|
||||
let s=format!(
|
||||
"Expected a map with a single field,\n\
|
||||
instead found both {{ \"{}\":... , \"{}\": ... }}",
|
||||
variant,
|
||||
second,
|
||||
);
|
||||
return Err(M::Error::custom(s));
|
||||
}
|
||||
Ok(WhichVariant{variant})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for WhichVariant{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(WhichVariantVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// Denotes this as a command type.
|
||||
pub trait CommandTrait:Serialize{
|
||||
type Returns:DeserializeOwned;
|
||||
}
|
||||
|
||||
impl CommandTrait for BasicCommand{
|
||||
type Returns=BasicRetVal;
|
||||
}
|
||||
|
||||
|
||||
/// Describes a command.
|
||||
#[repr(C)]
|
||||
#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize,StableAbi)]
|
||||
pub struct CommandDescription{
|
||||
/// A description of what this command does.
|
||||
pub name:RCow<'static,str>,
|
||||
/// A description of what this command does,
|
||||
/// optionally with a description of the command format.
|
||||
pub description:RCow<'static,str>,
|
||||
}
|
||||
|
||||
|
||||
impl CommandDescription{
|
||||
pub fn from_literals(
|
||||
name:&'static str,
|
||||
description:&'static str,
|
||||
)->Self{
|
||||
CommandDescription{
|
||||
name:name.into(),
|
||||
description:description.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug,Clone,PartialEq,Eq,StableAbi)]
|
||||
pub struct AsyncCommand{
|
||||
pub from:PluginId,
|
||||
pub which_plugin:WhichPlugin,
|
||||
pub command:RString,
|
||||
}
|
||||
127
__ffi/abi_stable_crates/interface/src/error.rs
Normal file
127
__ffi/abi_stable_crates/interface/src/error.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
use crate::{commands::CommandDescription,WhichPlugin,WhichCommandRet};
|
||||
|
||||
use abi_stable::{StableAbi, std_types::{RBoxError,RBox,RString,RVec}};
|
||||
use std::{error::Error as ErrorTrait, fmt::{self,Display}};
|
||||
use core_extensions::strings::StringExt;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug,StableAbi)]
|
||||
pub enum Error{
|
||||
/// An error produced by `serde_json::to_string`.
|
||||
Serialize(RBoxError,WhichCommandRet),
|
||||
/// An error produced by `serde_json::from_string`.
|
||||
Deserialize(RBoxError,WhichCommandRet),
|
||||
/// A deserialization error produced when trying to deserialize json
|
||||
/// as a particular command type.
|
||||
UnsupportedCommand(RBox<Unsupported>),
|
||||
/// A deserialization error produced when trying to deserialize json
|
||||
/// as a particular return value type.
|
||||
UnsupportedReturnValue(RBox<Unsupported>),
|
||||
/// An invalid plugin.
|
||||
InvalidPlugin(WhichPlugin),
|
||||
/// A custom error.
|
||||
Custom(RBoxError),
|
||||
/// A list of errors.
|
||||
Many(RVec<Error>),
|
||||
}
|
||||
|
||||
/// Represents a command or return value that wasn't supported.
|
||||
#[repr(C)]
|
||||
#[derive(Debug,StableAbi)]
|
||||
pub struct Unsupported{
|
||||
/// The name of the plugin for which the command/return value wasn't supported.
|
||||
pub plugin_name:RString,
|
||||
/// The command/return value that wasn't supported.
|
||||
pub command_name:RString,
|
||||
/// A custom error.
|
||||
pub error:RBoxError,
|
||||
/// A list of the commands that the plugin supports
|
||||
pub supported_commands:RVec<CommandDescription>,
|
||||
}
|
||||
|
||||
|
||||
impl Error{
|
||||
pub fn unsupported_command(what:Unsupported)->Self{
|
||||
Error::UnsupportedCommand(RBox::new(what))
|
||||
}
|
||||
pub fn unsupported_return_value(what:Unsupported)->Self{
|
||||
Error::UnsupportedReturnValue(RBox::new(what))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error{
|
||||
fn fmt(&self,f:&mut fmt::Formatter<'_>)->fmt::Result {
|
||||
match self {
|
||||
Error::Serialize(e,which)=>{
|
||||
let which=match which {
|
||||
WhichCommandRet::Command=>"command",
|
||||
WhichCommandRet::Return=>"return value",
|
||||
};
|
||||
writeln!(f,"Error happened while serializing the {}:\n{}\n",which,e)
|
||||
}
|
||||
Error::Deserialize(e,which)=>{
|
||||
let which=match which {
|
||||
WhichCommandRet::Command=>"command",
|
||||
WhichCommandRet::Return=>"return value",
|
||||
};
|
||||
writeln!(f,"Error happened while deserializing {}:\n{}\n",which,e)
|
||||
}
|
||||
Error::UnsupportedCommand(v)=>{
|
||||
writeln!(
|
||||
f,
|
||||
"Plugin '{}' ooes not support this command:\n\
|
||||
\t'{}'\n\
|
||||
Because of this error:\n{}\n\
|
||||
Supported commands:\
|
||||
",
|
||||
v.plugin_name,
|
||||
v.command_name,
|
||||
v.error,
|
||||
|
||||
)?;
|
||||
|
||||
for supported in &v.supported_commands {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
format!(
|
||||
"\nName:\n{}\nDescription:\n{}\n\n",
|
||||
supported.name.left_padder(4),
|
||||
supported.description.left_padder(4),
|
||||
).left_padder(4)
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Error::UnsupportedReturnValue(v)=>
|
||||
writeln!(
|
||||
f,
|
||||
"Unrecognized return value from '{}',named:\n\
|
||||
\t'{}'\n\
|
||||
Because of this error:\n{}\n\
|
||||
",
|
||||
v.plugin_name,
|
||||
v.command_name,
|
||||
v.error,
|
||||
),
|
||||
Error::InvalidPlugin(wc)=>
|
||||
writeln!(
|
||||
f,
|
||||
"Attempted to access a nonexistent plugin with the WhichPlugin:\n\t{:?}\n",
|
||||
wc
|
||||
),
|
||||
Error::Custom(e)=>Display::fmt(e,f),
|
||||
Error::Many(list)=>{
|
||||
for e in list {
|
||||
writeln!(f,"{}",e)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ErrorTrait for Error{}
|
||||
|
||||
234
__ffi/abi_stable_crates/interface/src/lib.rs
Normal file
234
__ffi/abi_stable_crates/interface/src/lib.rs
Normal file
@@ -0,0 +1,234 @@
|
||||
/*!
|
||||
This is an example `interface crate`,
|
||||
where all publically available modules(structs of function pointers) and types are declared,
|
||||
|
||||
To load the library and the modules together,
|
||||
call `<PluginMod_Ref as RootModule>::load_from_directory`,
|
||||
which will load the dynamic library from a directory(folder),
|
||||
and all the modules inside of the library.
|
||||
|
||||
*/
|
||||
|
||||
use abi_stable::{
|
||||
StableAbi,
|
||||
sabi_trait,
|
||||
package_version_strings,
|
||||
declare_root_module_statics,
|
||||
library::RootModule,
|
||||
sabi_types::VersionStrings,
|
||||
external_types::{
|
||||
crossbeam_channel::RSender,
|
||||
},
|
||||
std_types::{RBox, RCow, RVec, RStr, RString,RResult, ROption, ROk,RSome},
|
||||
};
|
||||
|
||||
use serde::{Serialize,Deserialize};
|
||||
|
||||
mod commands;
|
||||
mod error;
|
||||
mod which_plugin;
|
||||
mod vec_from_map;
|
||||
pub mod utils;
|
||||
|
||||
|
||||
pub use self::{
|
||||
commands::{
|
||||
BasicCommand,BasicRetVal,CommandDescription,CommandTrait,WhichVariant,AsyncCommand,
|
||||
},
|
||||
error::{Error,Unsupported},
|
||||
which_plugin::WhichPlugin,
|
||||
vec_from_map::VecFromMap,
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
The identifier for a plugin.
|
||||
*/
|
||||
#[repr(C)]
|
||||
#[derive(Debug,Clone,PartialEq,Eq,StableAbi,Serialize,Deserialize)]
|
||||
pub struct PluginId{
|
||||
pub named:RCow<'static,str>,
|
||||
/// The number of the instance of this Plugin.
|
||||
pub instance:u64,
|
||||
}
|
||||
|
||||
|
||||
/// Describes whether a boxed error is a command or a return value.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug,Clone,PartialEq,Eq,StableAbi,Serialize,Deserialize)]
|
||||
pub enum WhichCommandRet{
|
||||
Command,
|
||||
Return,
|
||||
}
|
||||
|
||||
|
||||
/// The response from having called `ApplicationMut::send_command_to_plugin` ealier.
|
||||
#[repr(C)]
|
||||
#[derive(Debug,Clone,PartialEq,Eq,StableAbi)]
|
||||
pub struct PluginResponse<'a>{
|
||||
/// The id of the plugin that is responding.
|
||||
pub plugin_id:PluginId,
|
||||
/// The response from the plugin
|
||||
pub response:RCow<'a,str>,
|
||||
}
|
||||
|
||||
|
||||
impl<'a> PluginResponse<'a>{
|
||||
pub fn owned_response(plugin_id:PluginId,response:RString)->Self{
|
||||
Self{plugin_id,response:response.into()}
|
||||
}
|
||||
pub fn borrowed_response(plugin_id:PluginId,response:RStr<'a>)->Self{
|
||||
Self{plugin_id,response:response.into()}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
pub type PluginType=Plugin_TO<'static,RBox<()>>;
|
||||
|
||||
|
||||
/**
|
||||
A plugin which is loaded by the application,and provides some functionality.
|
||||
|
||||
|
||||
*/
|
||||
#[sabi_trait]
|
||||
//#[sabi(debug_print)]
|
||||
pub trait Plugin {
|
||||
|
||||
/// Handles a JSON encoded command.
|
||||
fn json_command(
|
||||
&mut self,
|
||||
command: RStr<'_>,
|
||||
app:ApplicationMut<'_>,
|
||||
)->RResult<RString,Error>;
|
||||
|
||||
/// Handles a response from another Plugin,
|
||||
/// from having called `ApplicationMut::send_command_to_plugin` ealier.
|
||||
fn handle_response<'a>(
|
||||
&mut self,
|
||||
response:PluginResponse<'a>,
|
||||
_app:ApplicationMut<'_>,
|
||||
)->RResult<ROption<PluginResponse<'a>>,Error>{
|
||||
ROk(RSome(response))
|
||||
}
|
||||
|
||||
/// Gets the PluginId that was passed to this plugin in its constructor.
|
||||
fn plugin_id(&self)->&PluginId;
|
||||
|
||||
/// Gets a description of all commands from this Plugin.
|
||||
fn list_commands(&self)->RVec<CommandDescription>;
|
||||
|
||||
/*
|
||||
Closes the plugin,
|
||||
|
||||
This does not unload the dynamic library of this plugin,
|
||||
you can instantiate another instance of this plugin with
|
||||
`PluginMod_Ref::get_module().new()(application_handle)`.
|
||||
|
||||
|
||||
|
||||
The `#[sabi(last_prefix_field)]` attribute here means that this is the last method
|
||||
that was defined in the first compatible version of the library
|
||||
(0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
|
||||
requiring new methods to always be added below preexisting ones.
|
||||
|
||||
The `#[sabi(last_prefix_field)]` attribute would stay on this method until the library
|
||||
bumps its "major" version,
|
||||
at which point it would be moved to the last method at the time.
|
||||
*/
|
||||
#[sabi(last_prefix_field)]
|
||||
fn close(self,app:ApplicationMut<'_>);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// The root module of a`plugin` dynamic library.
|
||||
///
|
||||
/// To load this module,
|
||||
/// call <PluginMod as RootModule>::load_from_directory(some_directory_path)
|
||||
#[repr(C)]
|
||||
#[derive(StableAbi)]
|
||||
#[sabi(kind(Prefix(prefix_ref="PluginMod_Ref")))]
|
||||
#[sabi(missing_field(panic))]
|
||||
pub struct PluginMod {
|
||||
/**
|
||||
Constructs the plugin.
|
||||
|
||||
|
||||
The `#[sabi(last_prefix_field)]` attribute here means that this is the last field in this struct
|
||||
that was defined in the first compatible version of the library
|
||||
(0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
|
||||
requiring new fields to always be added below preexisting ones.
|
||||
|
||||
The `#[sabi(last_prefix_field)]` attribute would stay on this field until the library
|
||||
bumps its "major" version,
|
||||
at which point it would be moved to the last field at the time.
|
||||
|
||||
*/
|
||||
#[sabi(last_prefix_field)]
|
||||
pub new: extern "C" fn(RSender<AsyncCommand>,PluginId) -> RResult<PluginType,Error>,
|
||||
}
|
||||
|
||||
|
||||
impl RootModule for PluginMod_Ref {
|
||||
declare_root_module_statics!{PluginMod_Ref}
|
||||
const BASE_NAME: &'static str = "plugin";
|
||||
const NAME: &'static str = "plugin";
|
||||
const VERSION_STRINGS: VersionStrings = package_version_strings!();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// A mutable reference to the application implementation.
|
||||
pub type ApplicationMut<'a>=Application_TO<'a,&'a mut ()>;
|
||||
|
||||
|
||||
#[sabi_trait]
|
||||
pub trait Application{
|
||||
|
||||
/// Asynchronously Sends a command to the plugin(s) specified by `which_plugin`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an `Error::InvalidPlugin` if `which_plugin` is invalid.
|
||||
fn send_command_to_plugin(
|
||||
&mut self,
|
||||
from:&PluginId,
|
||||
which_plugin:WhichPlugin,
|
||||
command:RString,
|
||||
)->RResult<(),Error>;
|
||||
|
||||
/**
|
||||
Gets the `PluginId`s of the plugins specified by `which_plugin`.
|
||||
|
||||
|
||||
The `#[sabi(last_prefix_field)]` attribute here means that this is the last method
|
||||
that was defined in the first compatible version of the library
|
||||
(0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
|
||||
requiring new methods to always be added below preexisting ones.
|
||||
|
||||
The `#[sabi(last_prefix_field)]` attribute would stay on this method until the library
|
||||
bumps its "major" version,
|
||||
at which point it would be moved to the last method at the time.
|
||||
|
||||
*/
|
||||
#[sabi(last_prefix_field)]
|
||||
fn get_plugin_id(&self,which_plugin:WhichPlugin)->RResult<RVec<PluginId>,Error>;
|
||||
|
||||
/// Gets the sender end of a channel to send commands to the application/other plugins.
|
||||
fn sender(&self)->RSender<AsyncCommand>;
|
||||
|
||||
/// Gets the PluginId of all loaded plugins
|
||||
fn loaded_plugins(&self)->RVec<PluginId>;
|
||||
}
|
||||
129
__ffi/abi_stable_crates/interface/src/utils.rs
Normal file
129
__ffi/abi_stable_crates/interface/src/utils.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use crate::{
|
||||
commands::{
|
||||
BasicCommand,
|
||||
CommandUnion,
|
||||
CommandUnion as CU,
|
||||
CommandTrait,
|
||||
ReturnValUnion,
|
||||
ReturnValUnion as RVU,
|
||||
WhichVariant,
|
||||
BasicRetVal,
|
||||
},
|
||||
error::Unsupported,
|
||||
ApplicationMut,WhichCommandRet,Error,Plugin,PluginType,
|
||||
};
|
||||
use abi_stable::{std_types::{RStr, RString, RBoxError, RResult}};
|
||||
use serde::{Serialize,Deserialize};
|
||||
|
||||
|
||||
/**
|
||||
Sends a json encoded command to a plugin,and returns the response by encoding it to json.
|
||||
|
||||
# Errors
|
||||
|
||||
These are all error that this function returns
|
||||
(this does not include error returned as part of the command):
|
||||
|
||||
- Error::Serialize:
|
||||
If the command/return value could not be serialized to JSON.
|
||||
|
||||
- Error::Deserialize
|
||||
If the command/return value could not be deserialized from JSON(this comes from the plugin).
|
||||
|
||||
- Error::UnsupportedCommand
|
||||
If the command is not supported by the plugin.
|
||||
|
||||
*/
|
||||
pub fn process_command<'de,P,C,R,F>(this:&mut P,command:RStr<'de>,f:F)->RResult<RString,Error>
|
||||
where
|
||||
P:Plugin,
|
||||
F:FnOnce(&mut P,C)->Result<R,Error>,
|
||||
C:Deserialize<'de>,
|
||||
R:Serialize,
|
||||
{
|
||||
(||->Result<RString,Error>{
|
||||
let command=command.as_str();
|
||||
|
||||
let which_variant=serde_json::from_str::<WhichVariant>(&command)
|
||||
.map_err(|e| Error::Deserialize(RBoxError::new(e),WhichCommandRet::Command) )?;
|
||||
|
||||
let command=serde_json::from_str::<CommandUnion<C>>(command)
|
||||
.map_err(|e|{
|
||||
Error::unsupported_command(Unsupported{
|
||||
plugin_name:this.plugin_id().named.clone().into_owned(),
|
||||
command_name:which_variant.variant,
|
||||
error:RBoxError::new(e),
|
||||
supported_commands:this.list_commands(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let ret:ReturnValUnion<R>=match command {
|
||||
CU::Basic(BasicCommand::GetCommands)=>{
|
||||
let commands=this.list_commands();
|
||||
RVU::Basic(BasicRetVal::GetCommands(commands))
|
||||
}
|
||||
CU::ForPlugin(cmd)=>{
|
||||
RVU::ForPlugin(f(this,cmd)?)
|
||||
}
|
||||
};
|
||||
|
||||
match serde_json::to_string(&ret) {
|
||||
Ok(v)=>Ok(v.into()),
|
||||
Err(e)=>Err(Error::Serialize(RBoxError::new(e),WhichCommandRet::Return)),
|
||||
}
|
||||
})().into()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Sends a typed command to a plugin.
|
||||
|
||||
# Errors
|
||||
|
||||
These are all error that this function returns
|
||||
(this does not include error returned as part of the command):
|
||||
|
||||
- Error::Serialize:
|
||||
If the command/return value could not be serialized to JSON.
|
||||
|
||||
- Error::Deserialize
|
||||
If the command/return value could not be deserialized from JSON(this comes from the plugin).
|
||||
|
||||
- Error::UnsupportedReturnValue:
|
||||
If the return value could not be deserialized from JSON
|
||||
(after checking that it has the `{"name":"...",description: ... }` format),
|
||||
containing the name of the command this is a return value for .
|
||||
|
||||
- Error::UnsupportedCommand
|
||||
If the command is not supported by the plugin.
|
||||
|
||||
*/
|
||||
pub fn send_command<C>(
|
||||
this:&mut PluginType,
|
||||
command:&C,
|
||||
app:ApplicationMut<'_>
|
||||
)->Result<C::Returns,Error>
|
||||
where
|
||||
C:CommandTrait,
|
||||
{
|
||||
let cmd=serde_json::to_string(&command)
|
||||
.map_err(|e| Error::Serialize(RBoxError::new(e),WhichCommandRet::Command) )?;
|
||||
|
||||
let ret=this.json_command(RStr::from(&*cmd),app).into_result()?;
|
||||
|
||||
let which_variant=serde_json::from_str::<WhichVariant>(&*ret)
|
||||
.map_err(|e| Error::Deserialize(RBoxError::new(e),WhichCommandRet::Return) )?;
|
||||
|
||||
serde_json::from_str::<C::Returns>(&ret)
|
||||
.map_err(|e|{
|
||||
Error::unsupported_return_value(Unsupported{
|
||||
plugin_name:this.plugin_id().named.clone().into_owned(),
|
||||
command_name:which_variant.variant,
|
||||
error:RBoxError::new(e),
|
||||
supported_commands:this.list_commands(),
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
57
__ffi/abi_stable_crates/interface/src/vec_from_map.rs
Normal file
57
__ffi/abi_stable_crates/interface/src/vec_from_map.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use std::{fmt, marker::PhantomData};
|
||||
use serde::de::{Deserialize, Deserializer, Visitor, MapAccess};
|
||||
|
||||
|
||||
/// Used to deserialize a list of key-value pairs from a json object.
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct VecFromMap<K,V>{
|
||||
pub vec:Vec<(K,V)>,
|
||||
}
|
||||
|
||||
|
||||
struct VecFromMapVisitor<K, V> {
|
||||
marker: PhantomData<(K, V)>
|
||||
}
|
||||
|
||||
impl<K, V> VecFromMapVisitor<K, V> {
|
||||
const NEW:Self=Self{marker:PhantomData};
|
||||
}
|
||||
|
||||
impl<'de, K, V> Visitor<'de> for VecFromMapVisitor<K, V>
|
||||
where
|
||||
K: Deserialize<'de>,
|
||||
V: Deserialize<'de>,
|
||||
{
|
||||
type Value = VecFromMap<K, V>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let cap=access.size_hint().unwrap_or(0);
|
||||
let mut vec = Vec::<(K,V)>::with_capacity(cap);
|
||||
|
||||
while let Some(pair) = access.next_entry()? {
|
||||
vec.push(pair);
|
||||
}
|
||||
|
||||
Ok(VecFromMap{vec})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, K, V> Deserialize<'de> for VecFromMap<K, V>
|
||||
where
|
||||
K: Deserialize<'de>,
|
||||
V: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(VecFromMapVisitor::NEW)
|
||||
}
|
||||
}
|
||||
262
__ffi/abi_stable_crates/interface/src/which_plugin.rs
Normal file
262
__ffi/abi_stable_crates/interface/src/which_plugin.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
use crate::PluginId;
|
||||
|
||||
use std::{fmt::{self,Display}, str::FromStr};
|
||||
use arrayvec::ArrayVec;
|
||||
use abi_stable::{StableAbi, std_types::{RCow,RVec,RString,cow::BorrowingRCowStr}};
|
||||
use core_extensions::{StringExt,SelfOps};
|
||||
use serde::{Serialize,Deserialize,Deserializer,Serializer};
|
||||
|
||||
|
||||
/// A way to choose to which plugins one refers to when sending commands,and other operations.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug,Clone,PartialEq,Eq,StableAbi)]
|
||||
pub enum WhichPlugin{
|
||||
Id(PluginId),
|
||||
First{
|
||||
named:RCow<'static,str>,
|
||||
},
|
||||
Last{
|
||||
named:RCow<'static,str>,
|
||||
},
|
||||
Every{
|
||||
named:RCow<'static,str>,
|
||||
},
|
||||
Many(RVec<WhichPlugin>),
|
||||
}
|
||||
|
||||
|
||||
impl WhichPlugin{
|
||||
/// Converts this `WhichPlugin` to its json representation,
|
||||
/// generally used as a key in a json object.
|
||||
pub fn to_key(&self)->RString{
|
||||
let mut buffer=RString::new();
|
||||
self.write_key(&mut buffer);
|
||||
buffer
|
||||
}
|
||||
|
||||
/// Writes the value of this as a key usable in the application config.
|
||||
pub fn write_key(&self,buf:&mut RString){
|
||||
use std::fmt::Write;
|
||||
match self {
|
||||
WhichPlugin::Id(id)=>write!(buf,"{}:{}",id.named,id.instance).drop_(),
|
||||
WhichPlugin::First{named}=>write!(buf,"{}:first",named).drop_(),
|
||||
WhichPlugin::Last{named}=>write!(buf,"{}:last",named).drop_(),
|
||||
WhichPlugin::Every{named}=>write!(buf,"{}:every",named).drop_(),
|
||||
WhichPlugin::Many(list)=>{
|
||||
for elem in list {
|
||||
elem.write_key(buf);
|
||||
buf.push(',');
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl FromStr for WhichPlugin{
|
||||
type Err=WhichPluginError;
|
||||
|
||||
fn from_str(full_str:&str)->Result<Self,WhichPluginError>{
|
||||
let mut comma_sep=full_str.split(',').peekable();
|
||||
let first=comma_sep.next().unwrap_or("").piped(|s|Self::parse_single(s,full_str))?;
|
||||
|
||||
if comma_sep.peek().is_some() {
|
||||
let mut list:RVec<WhichPlugin>=vec![first].into();
|
||||
for s in comma_sep.filter(|s| !s.is_empty() ) {
|
||||
list.push( Self::parse_single(s,full_str)? );
|
||||
}
|
||||
WhichPlugin::Many(list)
|
||||
}else{
|
||||
first
|
||||
}.piped(Ok)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl WhichPlugin{
|
||||
|
||||
fn parse_single(s:&str,full_str:&str)->Result<Self,WhichPluginError>{
|
||||
let splitted=s.splitn(2,':').map(|s|s.trim()).collect::<ArrayVec<[&str;2]>>();
|
||||
let named=splitted.get(0)
|
||||
.filter(|s| !s.is_empty() )
|
||||
.ok_or_else(|| WhichPluginError(full_str.into()) )?
|
||||
.to_string()
|
||||
.into_(RCow::<'static,str>::T);
|
||||
let selector=splitted.get(1).map_or("",|x|*x);
|
||||
|
||||
match selector {
|
||||
"first"=>return Ok(WhichPlugin::First{named}),
|
||||
""|"last"=>return Ok(WhichPlugin::Last{named}),
|
||||
"all"|"every"=>return Ok(WhichPlugin::Every{named}),
|
||||
_=>(),
|
||||
}
|
||||
|
||||
let instance=selector.parse::<u64>().map_err(|_| WhichPluginError(full_str.into()) )?;
|
||||
Ok(WhichPlugin::Id(PluginId{named,instance}))
|
||||
}
|
||||
|
||||
pub const FMT_MSG:&'static str=r##"
|
||||
|
||||
"plugin name":
|
||||
refers to the last plugin named "plugin name".
|
||||
|
||||
"plugin name:10":
|
||||
refers to the 10th instance of the plugin named "plugin name".
|
||||
|
||||
"plugin name:first":
|
||||
refers to the first instance of the plugin named "plugin name".
|
||||
|
||||
"plugin name:last":
|
||||
refers to the last instance of the plugin named "plugin name".
|
||||
|
||||
"plugin name:every":
|
||||
refers to all the instances of the plugin named "plugin name".
|
||||
|
||||
"plugin name 1,plugin name 2:first,plugin name 3:every":
|
||||
refers to the last instance of the plugin named "plugin name 1".
|
||||
refers to the first instance of the plugin named "plugin name 2".
|
||||
refers to the all the instances of the plugin named "plugin name 3".
|
||||
|
||||
Plugin names:
|
||||
|
||||
- Are trimmed,so you can add spaces at the start and the end.
|
||||
|
||||
- Cannot contain commas,since they will be interpreted as a list of plugins.
|
||||
|
||||
|
||||
"##;
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl<'de> Deserialize<'de> for WhichPlugin{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de;
|
||||
BorrowingRCowStr::deserialize(deserializer)?
|
||||
.cow
|
||||
.parse::<Self>()
|
||||
.map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Serialize for WhichPlugin{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.to_key().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug,Clone,StableAbi)]
|
||||
pub struct WhichPluginError(RString);
|
||||
|
||||
|
||||
impl Display for WhichPluginError{
|
||||
fn fmt(&self,f:&mut fmt::Formatter)->fmt::Result{
|
||||
writeln!(
|
||||
f,
|
||||
"Could not parse this as a `WhichPlugin`:\n\t'{}'\nExpected format:\n{}\n",
|
||||
self.0,
|
||||
WhichPlugin::FMT_MSG.left_padder(4),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests{
|
||||
use super::*;
|
||||
|
||||
fn new_str_expected()->Vec<(&'static str,WhichPlugin)>{
|
||||
vec![
|
||||
(
|
||||
"plugin name",
|
||||
WhichPlugin::Last{named:"plugin name".into()}
|
||||
),
|
||||
(
|
||||
"plugin name:10",
|
||||
WhichPlugin::Id(PluginId{
|
||||
named:"plugin name".into(),
|
||||
instance:10,
|
||||
})
|
||||
),
|
||||
(
|
||||
"plugin name:first",
|
||||
WhichPlugin::First{named:"plugin name".into()}
|
||||
),
|
||||
(
|
||||
"plugin name:last",
|
||||
WhichPlugin::Last{named:"plugin name".into()}
|
||||
),
|
||||
(
|
||||
"plugin name:every",
|
||||
WhichPlugin::Every{named:"plugin name".into()}
|
||||
),
|
||||
(
|
||||
"plugin name 1,plugin name 2:first,plugin name 3:every",
|
||||
WhichPlugin::Many(vec![
|
||||
WhichPlugin::Last{named:"plugin name 1".into()},
|
||||
WhichPlugin::First{named:"plugin name 2".into()},
|
||||
WhichPlugin::Every{named:"plugin name 3".into()},
|
||||
].into())
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_correctly(){
|
||||
let str_expected=new_str_expected();
|
||||
|
||||
for (str_,expected) in str_expected {
|
||||
let parsed=str_.parse::<WhichPlugin>().unwrap();
|
||||
assert_eq!(parsed,expected);
|
||||
|
||||
assert_eq!(
|
||||
parsed.to_key().parse::<WhichPlugin>().unwrap(),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn serde_(){
|
||||
let str_expected=new_str_expected();
|
||||
|
||||
for (_,elem) in str_expected {
|
||||
let str_=serde_json::to_string(&elem).unwrap();
|
||||
let other:WhichPlugin=serde_json::from_str(&str_)
|
||||
.unwrap_or_else(|e| panic!("{}",e) );
|
||||
assert_eq!(other,elem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parses_incorrectly(){
|
||||
let list=vec![
|
||||
// An empty plugin name is invalid
|
||||
"",
|
||||
":",
|
||||
":first",
|
||||
":last",
|
||||
",",
|
||||
",,,:first,:last",
|
||||
];
|
||||
|
||||
for str_ in list {
|
||||
str_.parse::<WhichPlugin>().unwrap_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
366
__ffi/abi_stable_crates/plugin_0/Cargo.lock
generated
Normal file
366
__ffi/abi_stable_crates/plugin_0/Cargo.lock
generated
Normal file
@@ -0,0 +1,366 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "abi_stable"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112fbc83f18cf0f12358975cac27eb4556cc5d777bde72312e0cec7a294e1699"
|
||||
dependencies = [
|
||||
"abi_stable_derive",
|
||||
"abi_stable_shared",
|
||||
"core_extensions",
|
||||
"crossbeam-channel",
|
||||
"generational-arena",
|
||||
"libloading",
|
||||
"lock_api",
|
||||
"parking_lot",
|
||||
"repr_offset",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abi_stable_derive"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1be19f4da4a4a1f2537f1f14d8f0917c07758acb7419d3bad9e9d62a11f6b5b0"
|
||||
dependencies = [
|
||||
"abi_stable_shared",
|
||||
"as_derive_utils",
|
||||
"core_extensions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"typed-arena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abi_stable_shared"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96112d6a13f37dd1cc2a2f07f7f12b7c3d176b6eec36e7987432a70fece8b48"
|
||||
dependencies = [
|
||||
"core_extensions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "as_derive_utils"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc3613a62c7b739739a2cb1ee166ac275f16c5b86caf454ba21a2f79f04b025"
|
||||
dependencies = [
|
||||
"core_extensions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "core_extensions"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff55d62b113b98e0b0a55112741376c4cfe36f1290d18392c2c5727e09629a7f"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example_interface"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"arrayvec",
|
||||
"core_extensions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example_plugin_text_munging"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"core_extensions",
|
||||
"example_interface",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generational-arena"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "repr_offset"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a68e33940e1f1e7ab77493ad7d983dba544ce72185c61d4b1b23e6c32dd2a165"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
16
__ffi/abi_stable_crates/plugin_0/Cargo.toml
Normal file
16
__ffi/abi_stable_crates/plugin_0/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "example_plugin_text_munging"
|
||||
version = "0.1.0"
|
||||
authors = ["rodrimati1992 <rodrimatt1985@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
abi_stable = "0.9.1"
|
||||
core_extensions={ version = "0.1.18", default_features = false, features = ["std"] }
|
||||
serde={ version = "1.0.117", features = ["derive"] }
|
||||
serde_json= "1.0.59"
|
||||
example_interface={ version="0.1", path="../interface"}
|
||||
|
||||
[lib]
|
||||
name = "example_plugin_text_munging"
|
||||
crate-type = ["cdylib",'rlib']
|
||||
161
__ffi/abi_stable_crates/plugin_0/src/lib.rs
Normal file
161
__ffi/abi_stable_crates/plugin_0/src/lib.rs
Normal file
@@ -0,0 +1,161 @@
|
||||
/*!
|
||||
This is an `implementation crate`,
|
||||
It exports the root module(a struct of function pointers) required by the
|
||||
`example_0_interface`(the `interface crate`).
|
||||
|
||||
*/
|
||||
|
||||
use std::collections::HashSet;
|
||||
use abi_stable::{
|
||||
export_root_module,
|
||||
sabi_extern_fn,
|
||||
external_types::crossbeam_channel::RSender,
|
||||
prefix_type::PrefixTypeTrait,
|
||||
sabi_trait::prelude::TU_Opaque,
|
||||
std_types::{RStr,RVec, RString,RResult,ROk},
|
||||
};
|
||||
use example_interface::{
|
||||
AsyncCommand,
|
||||
ApplicationMut,
|
||||
CommandDescription,
|
||||
Error as AppError,
|
||||
Plugin,PluginType,PluginId,PluginMod,PluginMod_Ref,
|
||||
Plugin_TO,
|
||||
utils::process_command,
|
||||
};
|
||||
use core_extensions::{SelfOps,StringExt};
|
||||
use serde::{Serialize,Deserialize};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// Exports the root module of this library.
|
||||
///
|
||||
/// This code isn't run until the layout of the type it returns is checked.
|
||||
#[export_root_module]
|
||||
fn instantiate_root_module()->PluginMod_Ref{
|
||||
PluginMod {
|
||||
new,
|
||||
}.leak_into_prefix()
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// Instantiates the plugin.
|
||||
#[sabi_extern_fn]
|
||||
pub fn new(_sender:RSender<AsyncCommand>,plugin_id:PluginId) -> RResult<PluginType,AppError> {
|
||||
let this=TextMunging{
|
||||
plugin_id,
|
||||
};
|
||||
ROk(Plugin_TO::from_value(this,TU_Opaque))
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#[derive(Debug,Serialize,Deserialize)]
|
||||
pub enum ProcessTextCmd{
|
||||
Rot13(String),
|
||||
CapitalizeWords{
|
||||
text:String,
|
||||
words:HashSet<String>
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug,Serialize,Deserialize)]
|
||||
pub enum ReturnValue{
|
||||
Rot13(String),
|
||||
CapitalizeWords(String),
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
fn run_command_inner(
|
||||
_this:&mut TextMunging,
|
||||
command:ProcessTextCmd,
|
||||
_app:ApplicationMut<'_>,
|
||||
)->Result<ReturnValue,AppError>{
|
||||
match command {
|
||||
ProcessTextCmd::Rot13(text)=>{
|
||||
pub fn rot13(this:char)-> char{
|
||||
match this{
|
||||
v@'a'..='z'=>
|
||||
((((v as u8 - b'a')+13)%26)+b'a')as char,
|
||||
v@'A'..='Z'=>
|
||||
((((v as u8 - b'A')+13)%26)+b'A')as char,
|
||||
v=>v
|
||||
}
|
||||
}
|
||||
text.chars()
|
||||
.map(rot13)
|
||||
.collect::<String>()
|
||||
.piped(ReturnValue::Rot13)
|
||||
}
|
||||
ProcessTextCmd::CapitalizeWords{text,words}=>{
|
||||
let mut buffer=String::with_capacity(10);
|
||||
|
||||
for kv in text.split_while(|c|c.is_alphabetic()) {
|
||||
let str_=kv.str;
|
||||
let is_a_word=kv.key;
|
||||
if is_a_word && words.contains(str_) {
|
||||
buffer.extend(str_.chars().flat_map(char::to_uppercase));
|
||||
}else{
|
||||
buffer.push_str(str_);
|
||||
}
|
||||
}
|
||||
ReturnValue::CapitalizeWords(buffer)
|
||||
}
|
||||
}.piped(Ok)
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
struct TextMunging{
|
||||
plugin_id:PluginId,
|
||||
}
|
||||
|
||||
|
||||
impl Plugin for TextMunging {
|
||||
fn json_command(
|
||||
&mut self,
|
||||
command: RStr<'_>,
|
||||
app:ApplicationMut<'_>,
|
||||
)->RResult<RString,AppError>{
|
||||
process_command(self,command,|this,command:ProcessTextCmd|{
|
||||
run_command_inner(this,command,app)
|
||||
})
|
||||
}
|
||||
|
||||
fn plugin_id(&self)->&PluginId{
|
||||
&self.plugin_id
|
||||
}
|
||||
|
||||
fn list_commands(&self)->RVec<CommandDescription>{
|
||||
vec![
|
||||
CommandDescription::from_literals(
|
||||
"Rot13",
|
||||
"Uses the rot13 algorithm to hide spoilers in plain text."
|
||||
),
|
||||
CommandDescription::from_literals(
|
||||
"CapitalizeWords",
|
||||
"Capitalizes the words specified in words.\n\
|
||||
\n\
|
||||
Command parans:\n\
|
||||
{\"text\":\"text here\",words:[\"word0\",\"word1\"]}",
|
||||
),
|
||||
].into()
|
||||
}
|
||||
|
||||
fn close(self,_app:ApplicationMut<'_>){}
|
||||
}
|
||||
|
||||
366
__ffi/abi_stable_crates/plugin_1/Cargo.lock
generated
Normal file
366
__ffi/abi_stable_crates/plugin_1/Cargo.lock
generated
Normal file
@@ -0,0 +1,366 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "abi_stable"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112fbc83f18cf0f12358975cac27eb4556cc5d777bde72312e0cec7a294e1699"
|
||||
dependencies = [
|
||||
"abi_stable_derive",
|
||||
"abi_stable_shared",
|
||||
"core_extensions",
|
||||
"crossbeam-channel",
|
||||
"generational-arena",
|
||||
"libloading",
|
||||
"lock_api",
|
||||
"parking_lot",
|
||||
"repr_offset",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abi_stable_derive"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1be19f4da4a4a1f2537f1f14d8f0917c07758acb7419d3bad9e9d62a11f6b5b0"
|
||||
dependencies = [
|
||||
"abi_stable_shared",
|
||||
"as_derive_utils",
|
||||
"core_extensions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"typed-arena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abi_stable_shared"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96112d6a13f37dd1cc2a2f07f7f12b7c3d176b6eec36e7987432a70fece8b48"
|
||||
dependencies = [
|
||||
"core_extensions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "as_derive_utils"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc3613a62c7b739739a2cb1ee166ac275f16c5b86caf454ba21a2f79f04b025"
|
||||
dependencies = [
|
||||
"core_extensions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "core_extensions"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff55d62b113b98e0b0a55112741376c4cfe36f1290d18392c2c5727e09629a7f"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example_interface"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"arrayvec",
|
||||
"core_extensions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example_plugin_utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"core_extensions",
|
||||
"example_interface",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generational-arena"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "repr_offset"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a68e33940e1f1e7ab77493ad7d983dba544ce72185c61d4b1b23e6c32dd2a165"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
16
__ffi/abi_stable_crates/plugin_1/Cargo.toml
Normal file
16
__ffi/abi_stable_crates/plugin_1/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "example_plugin_utils"
|
||||
version = "0.1.0"
|
||||
authors = ["rodrimati1992 <rodrimatt1985@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
abi_stable = "0.9.1"
|
||||
core_extensions={ version = "0.1.18", default_features = false, features = ["std"] }
|
||||
serde={ version = "1.0.117", features = ["derive"] }
|
||||
serde_json = { version = "1.0.59", features = ["raw_value"] }
|
||||
example_interface={ version="0.1", path="../interface"}
|
||||
|
||||
[lib]
|
||||
name = "example_plugin_utils"
|
||||
crate-type = ["cdylib",'rlib']
|
||||
174
__ffi/abi_stable_crates/plugin_1/src/lib.rs
Normal file
174
__ffi/abi_stable_crates/plugin_1/src/lib.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
/*!
|
||||
This is an `implementation crate`,
|
||||
It exports the root module(a struct of function pointers) required by the
|
||||
`example_0_interface`(the `interface crate`).
|
||||
*/
|
||||
|
||||
use abi_stable::{
|
||||
export_root_module,
|
||||
sabi_extern_fn,
|
||||
external_types::crossbeam_channel::RSender,
|
||||
prefix_type::PrefixTypeTrait,
|
||||
sabi_trait::prelude::TU_Opaque,
|
||||
std_types::{RStr,RVec, RString,RResult,ROk},
|
||||
};
|
||||
use example_interface::{
|
||||
AsyncCommand,
|
||||
ApplicationMut,
|
||||
CommandDescription,
|
||||
Error as AppError,
|
||||
Plugin,PluginType,PluginId,PluginMod,PluginMod_Ref,
|
||||
Plugin_TO,
|
||||
utils::process_command,
|
||||
WhichPlugin,
|
||||
};
|
||||
use core_extensions::SelfOps;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde_json::value::Value;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// Exports the root module of this library.
|
||||
///
|
||||
/// This code isn't run until the layout of the type it returns is checked.
|
||||
#[export_root_module]
|
||||
fn instantiate_root_module()->PluginMod_Ref{
|
||||
PluginMod {
|
||||
new,
|
||||
}.leak_into_prefix()
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#[sabi_extern_fn]
|
||||
pub fn new(_sender:RSender<AsyncCommand>,plugin_id:PluginId) -> RResult<PluginType,AppError> {
|
||||
let this=CommandUtils{
|
||||
plugin_id,
|
||||
};
|
||||
ROk(Plugin_TO::from_value(this,TU_Opaque))
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#[derive(Debug,Serialize,Deserialize)]
|
||||
pub enum UtilCmd{
|
||||
Repeat{
|
||||
how_much:usize,
|
||||
plugin:WhichPlugin,
|
||||
command:Value,
|
||||
},
|
||||
Batch{
|
||||
plugin:WhichPlugin,
|
||||
commands:Vec<Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug,Serialize,Deserialize)]
|
||||
pub enum ReturnValue{
|
||||
Repeat,
|
||||
Batch,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
fn run_command_inner(
|
||||
this:&mut CommandUtils,
|
||||
command:UtilCmd,
|
||||
mut app:ApplicationMut<'_>,
|
||||
)->Result<ReturnValue,AppError>{
|
||||
match command {
|
||||
UtilCmd::Repeat{how_much,plugin,command}=>{
|
||||
let s=serde_json::to_string(&command).unwrap();
|
||||
for _ in 0..how_much {
|
||||
app.send_command_to_plugin(
|
||||
this.plugin_id(),
|
||||
plugin.clone(),
|
||||
s.clone().into(),
|
||||
).into_result()?;
|
||||
}
|
||||
ReturnValue::Repeat
|
||||
}
|
||||
UtilCmd::Batch{plugin,commands}=>{
|
||||
for command in commands {
|
||||
let s=serde_json::to_string(&command).unwrap();
|
||||
app.send_command_to_plugin(
|
||||
this.plugin_id(),
|
||||
plugin.clone(),
|
||||
s.into(),
|
||||
).into_result()?;
|
||||
}
|
||||
ReturnValue::Batch
|
||||
}
|
||||
}.piped(Ok)
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
struct CommandUtils{
|
||||
plugin_id:PluginId,
|
||||
}
|
||||
|
||||
|
||||
impl Plugin for CommandUtils {
|
||||
fn json_command(
|
||||
&mut self,
|
||||
command: RStr<'_>,
|
||||
app:ApplicationMut<'_>,
|
||||
)->RResult<RString,AppError>{
|
||||
process_command(self,command,|this,command|{
|
||||
run_command_inner(this,command,app)
|
||||
})
|
||||
}
|
||||
|
||||
fn plugin_id(&self)->&PluginId{
|
||||
&self.plugin_id
|
||||
}
|
||||
|
||||
fn list_commands(&self)->RVec<CommandDescription>{
|
||||
vec![
|
||||
CommandDescription::from_literals(
|
||||
"Repeat",
|
||||
"\
|
||||
Sends a command to a plugin N times.
|
||||
|
||||
Command params:
|
||||
{
|
||||
\"how_much\":10,
|
||||
\"plugin\":\"plugin:last\",
|
||||
\"command\":{ ... some command ... }
|
||||
}
|
||||
"
|
||||
),
|
||||
CommandDescription::from_literals(
|
||||
"Batch",
|
||||
"\
|
||||
Sends a sequence of commands to a plugin.
|
||||
|
||||
Command params:
|
||||
{
|
||||
\"plugin\":\"plugin_name\",
|
||||
\"commands\":[
|
||||
{ ... some command ... },
|
||||
{ ... some command ... },
|
||||
{ ... some command ... }
|
||||
]
|
||||
}",
|
||||
),
|
||||
].into()
|
||||
}
|
||||
|
||||
fn close(self,_app:ApplicationMut<'_>){}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user