feat: add abi stable crates
This commit is contained in:
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user