feat: add abi stable crates

This commit is contained in:
2020-12-27 14:43:46 +08:00
parent cd4a02f513
commit 8d34b85a9c
21 changed files with 3072 additions and 0 deletions

View 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"

View 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" }

View 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"]
}}
]
}}
}
}

View 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()
}
}
////////////////////////////////////////////////////////////////////////////////

View 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(())
}

View 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)
}
}