use std::fmt::{Display, Formatter}; use bincode::{Decode, Encode}; use paste::paste; pub trait RequestDefinition: Encode { type Response: ResponseDefinition; fn into_request(self) -> IPCRequest; } pub trait ResponseDefinition: Sized + Decode { type Error: std::error::Error; fn from_response(response: IPCResponse) -> Option>; fn into_response(self) -> IPCResponse; } macro_rules! define { ($(@$name:ident { $($req_n:ident: $req_t:ty),* } => { $($resp_n:ident: $resp_t:ty)* } | $($error:ident),*)*) => { $( paste! { #[derive(Encode, Decode)] pub struct [<$name Request>] { $(pub $req_n: $req_t),* } impl RequestDefinition for [<$name Request>] { type Response = [<$name Response>]; fn into_request(self) -> IPCRequest { IPCRequest::$name(self) } } #[derive(Encode, Decode)] pub struct [<$name Response>] { $(pub $resp_n: $resp_t),* } impl ResponseDefinition for [<$name Response>] { type Error = [<$name Error>]; fn from_response(response: IPCResponse) -> Option> { match response { IPCResponse::$name(inner) => Some(inner), _ => None, } } fn into_response(self) -> IPCResponse { IPCResponse::$name(Ok(self)) } } #[derive(Encode, Decode, Debug)] pub enum [<$name Error>] { $($error,)* } impl Display for [<$name Error>] { #[allow(unused)] fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match *self { $(Self::$error => f.write_str(stringify!($error)),)* } } } impl std::error::Error for [<$name Error>] {} } )* #[derive(Encode, Decode)] pub enum IPCRequest { $( $name(paste!([<$name Request>])), )* } #[derive(Encode, Decode)] pub enum IPCResponse { $( $name(Result]), paste!([<$name Error>])>), )* } }; } define! { @ListAccounts {} => { names: Vec } | @GetAccount { name: String } => { scopes: Vec } | NotFound @DeleteAccount { name: String } => {} | @CreateInvite {} => { link: String } | @UpdateScopes { account: String, scopes: Vec } => {} | NotFound }