mirror of
https://github.com/Start9Labs/rpc-toolkit.git
synced 2026-03-26 02:11:56 +00:00
pass through raw params
This commit is contained in:
@@ -1,382 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use clap::{ArgMatches, CommandFactory, FromArgMatches};
|
|
||||||
use futures::future::BoxFuture;
|
|
||||||
use futures::FutureExt;
|
|
||||||
use imbl_value::Value;
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use serde::ser::Serialize;
|
|
||||||
use yajrc::RpcError;
|
|
||||||
|
|
||||||
use crate::util::{extract, Flat, PhantomData};
|
|
||||||
|
|
||||||
/// Stores a command's implementation for a given context
|
|
||||||
/// Can be created from anything that implements ParentCommand, AsyncCommand, or SyncCommand
|
|
||||||
pub struct DynCommand<Context: crate::Context> {
|
|
||||||
pub(crate) name: &'static str,
|
|
||||||
pub(crate) metadata: Context::Metadata,
|
|
||||||
pub(crate) implementation: Option<Implementation<Context>>,
|
|
||||||
pub(crate) cli: Option<CliBindings<Context>>,
|
|
||||||
pub(crate) subcommands: Vec<Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct Implementation<Context> {
|
|
||||||
pub(crate) async_impl: Arc<
|
|
||||||
dyn Fn(Context, Vec<&'static str>, Value) -> BoxFuture<'static, Result<Value, RpcError>>
|
|
||||||
+ Send
|
|
||||||
+ Sync,
|
|
||||||
>,
|
|
||||||
pub(crate) sync_impl:
|
|
||||||
Box<dyn Fn(Context, Vec<&'static str>, Value) -> Result<Value, RpcError> + Send + Sync>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct CliBindings<Context> {
|
|
||||||
pub(crate) cmd: clap::Command,
|
|
||||||
pub(crate) parser: Box<dyn for<'a> Fn(&'a ArgMatches) -> Result<Value, RpcError> + Send + Sync>,
|
|
||||||
pub(crate) display: Option<
|
|
||||||
Box<
|
|
||||||
dyn Fn(Context, Vec<&'static str>, Value, Value) -> Result<(), imbl_value::Error>
|
|
||||||
+ Send
|
|
||||||
+ Sync,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
impl<Context: crate::Context> CliBindings<Context> {
|
|
||||||
pub(crate) fn from_parent<Cmd: FromArgMatches + CommandFactory + Serialize>() -> Self {
|
|
||||||
Self {
|
|
||||||
cmd: Cmd::command(),
|
|
||||||
parser: Box::new(|matches| {
|
|
||||||
imbl_value::to_value(&Cmd::from_arg_matches(matches).map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::INVALID_PARAMS_ERROR
|
|
||||||
})?)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::INVALID_PARAMS_ERROR
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
display: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn from_leaf<Cmd: FromArgMatches + CommandFactory + Serialize + LeafCommand<Context>>() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
display: Some(Box::new(|ctx, parent_method, params, res| {
|
|
||||||
let parent_params = imbl_value::from_value(params.clone())?;
|
|
||||||
Ok(imbl_value::from_value::<Cmd>(params)?.display(
|
|
||||||
ctx,
|
|
||||||
ParentInfo {
|
|
||||||
method: parent_method,
|
|
||||||
params: parent_params,
|
|
||||||
},
|
|
||||||
imbl_value::from_value(res)?,
|
|
||||||
))
|
|
||||||
})),
|
|
||||||
..Self::from_parent::<Cmd>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Must be implemented for all commands
|
|
||||||
/// Use `Parent = NoParent` if the implementation requires no arguments from the parent command
|
|
||||||
pub trait Command: DeserializeOwned + Sized + Send {
|
|
||||||
const NAME: &'static str;
|
|
||||||
type Parent: Command;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Includes the parent method, and the arguments requested from the parent
|
|
||||||
/// Arguments are flattened out in the params object, so ensure that there are no collisions between the names of the arguments for your method and its parents
|
|
||||||
pub struct ParentInfo<T> {
|
|
||||||
pub method: Vec<&'static str>,
|
|
||||||
pub params: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is automatically generated from a command based on its Parents.
|
|
||||||
/// It can be used to generate a proof that one of the parents contains the necessary arguments that a subcommand requires.
|
|
||||||
pub struct ParentChain<Cmd: Command>(PhantomData<Cmd>);
|
|
||||||
pub struct Contains<T>(PhantomData<T>);
|
|
||||||
impl Contains<NoParent> {
|
|
||||||
pub fn none() -> Self {
|
|
||||||
Self(PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T, U> From<(Contains<T>, Contains<U>)> for Contains<Flat<T, U>> {
|
|
||||||
fn from(_: (Contains<T>, Contains<U>)) -> Self {
|
|
||||||
Self(PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use this as a Parent if your command does not require any arguments from its parents
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct NoParent {}
|
|
||||||
impl Command for NoParent {
|
|
||||||
const NAME: &'static str = "";
|
|
||||||
type Parent = NoParent;
|
|
||||||
}
|
|
||||||
impl<Cmd> ParentChain<Cmd>
|
|
||||||
where
|
|
||||||
Cmd: Command,
|
|
||||||
{
|
|
||||||
pub fn child(&self) -> Contains<Cmd> {
|
|
||||||
Contains(PhantomData)
|
|
||||||
}
|
|
||||||
pub fn parent(&self) -> ParentChain<Cmd::Parent> {
|
|
||||||
ParentChain(PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement this for a command that has no implementation, but simply exists to organize subcommands
|
|
||||||
pub trait ParentCommand<Context: crate::Context>: Command {
|
|
||||||
fn metadata() -> Context::Metadata {
|
|
||||||
Context::Metadata::default()
|
|
||||||
}
|
|
||||||
fn subcommands(chain: ParentChain<Self>) -> Vec<DynCommand<Context>>;
|
|
||||||
}
|
|
||||||
impl<Context: crate::Context> DynCommand<Context> {
|
|
||||||
pub fn from_parent<
|
|
||||||
Cmd: ParentCommand<Context> + FromArgMatches + CommandFactory + Serialize,
|
|
||||||
>(
|
|
||||||
contains: Contains<Cmd::Parent>,
|
|
||||||
) -> Self {
|
|
||||||
drop(contains);
|
|
||||||
Self {
|
|
||||||
name: Cmd::NAME,
|
|
||||||
metadata: Cmd::metadata(),
|
|
||||||
implementation: None,
|
|
||||||
cli: Some(CliBindings::from_parent::<Cmd>()),
|
|
||||||
subcommands: Cmd::subcommands(ParentChain::<Cmd>(PhantomData)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_parent_no_cli<Cmd: ParentCommand<Context>>(
|
|
||||||
contains: Contains<Cmd::Parent>,
|
|
||||||
) -> Self {
|
|
||||||
drop(contains);
|
|
||||||
Self {
|
|
||||||
name: Cmd::NAME,
|
|
||||||
metadata: Cmd::metadata(),
|
|
||||||
implementation: None,
|
|
||||||
cli: None,
|
|
||||||
subcommands: Cmd::subcommands(ParentChain::<Cmd>(PhantomData)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement this for any command with an implementation
|
|
||||||
pub trait LeafCommand<Context: crate::Context>: Command {
|
|
||||||
type Ok: DeserializeOwned + Serialize + Send;
|
|
||||||
type Err: From<RpcError> + Into<RpcError> + Send;
|
|
||||||
fn metadata() -> Context::Metadata {
|
|
||||||
Context::Metadata::default()
|
|
||||||
}
|
|
||||||
fn display(self, ctx: Context, parent: ParentInfo<Self::Parent>, res: Self::Ok);
|
|
||||||
fn subcommands(chain: ParentChain<Self>) -> Vec<DynCommand<Context>> {
|
|
||||||
drop(chain);
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement this if your Command's implementation is async
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
pub trait AsyncCommand<Context: crate::Context>: LeafCommand<Context> {
|
|
||||||
async fn implementation(
|
|
||||||
self,
|
|
||||||
ctx: Context,
|
|
||||||
parent: ParentInfo<Self::Parent>,
|
|
||||||
) -> Result<Self::Ok, Self::Err>;
|
|
||||||
}
|
|
||||||
impl<Context: crate::Context> Implementation<Context> {
|
|
||||||
fn for_async<Cmd: AsyncCommand<Context>>(contains: Contains<Cmd::Parent>) -> Self {
|
|
||||||
drop(contains);
|
|
||||||
Self {
|
|
||||||
async_impl: Arc::new(|ctx, parent_method, params| {
|
|
||||||
async move {
|
|
||||||
let parent_params = extract::<Cmd::Parent>(¶ms)?;
|
|
||||||
imbl_value::to_value(
|
|
||||||
&extract::<Cmd>(¶ms)?
|
|
||||||
.implementation(
|
|
||||||
ctx,
|
|
||||||
ParentInfo {
|
|
||||||
method: parent_method,
|
|
||||||
params: parent_params,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.into())?,
|
|
||||||
)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::PARSE_ERROR
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}),
|
|
||||||
sync_impl: Box::new(|ctx, parent_method, params| {
|
|
||||||
let parent_params = extract::<Cmd::Parent>(¶ms)?;
|
|
||||||
imbl_value::to_value(
|
|
||||||
&ctx.runtime()
|
|
||||||
.block_on(
|
|
||||||
extract::<Cmd>(¶ms)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::INVALID_PARAMS_ERROR
|
|
||||||
})?
|
|
||||||
.implementation(
|
|
||||||
ctx,
|
|
||||||
ParentInfo {
|
|
||||||
method: parent_method,
|
|
||||||
params: parent_params,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map_err(|e| e.into())?,
|
|
||||||
)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::PARSE_ERROR
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<Context: crate::Context> DynCommand<Context> {
|
|
||||||
pub fn from_async<Cmd: AsyncCommand<Context> + FromArgMatches + CommandFactory + Serialize>(
|
|
||||||
contains: Contains<Cmd::Parent>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
name: Cmd::NAME,
|
|
||||||
metadata: Cmd::metadata(),
|
|
||||||
implementation: Some(Implementation::for_async::<Cmd>(contains)),
|
|
||||||
cli: Some(CliBindings::from_leaf::<Cmd>()),
|
|
||||||
subcommands: Cmd::subcommands(ParentChain::<Cmd>(PhantomData)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_async_no_cli<Cmd: AsyncCommand<Context>>(contains: Contains<Cmd::Parent>) -> Self {
|
|
||||||
Self {
|
|
||||||
name: Cmd::NAME,
|
|
||||||
metadata: Cmd::metadata(),
|
|
||||||
implementation: Some(Implementation::for_async::<Cmd>(contains)),
|
|
||||||
cli: None,
|
|
||||||
subcommands: Cmd::subcommands(ParentChain::<Cmd>(PhantomData)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement this if your Command's implementation is not async
|
|
||||||
pub trait SyncCommand<Context: crate::Context>: LeafCommand<Context> {
|
|
||||||
const BLOCKING: bool;
|
|
||||||
fn implementation(
|
|
||||||
self,
|
|
||||||
ctx: Context,
|
|
||||||
parent: ParentInfo<Self::Parent>,
|
|
||||||
) -> Result<Self::Ok, Self::Err>;
|
|
||||||
}
|
|
||||||
impl<Context: crate::Context> Implementation<Context> {
|
|
||||||
fn for_sync<Cmd: SyncCommand<Context>>(contains: Contains<Cmd::Parent>) -> Self {
|
|
||||||
drop(contains);
|
|
||||||
Self {
|
|
||||||
async_impl: if Cmd::BLOCKING {
|
|
||||||
Arc::new(|ctx, parent_method, params| {
|
|
||||||
tokio::task::spawn_blocking(move || {
|
|
||||||
let parent_params = extract::<Cmd::Parent>(¶ms)?;
|
|
||||||
imbl_value::to_value(
|
|
||||||
&extract::<Cmd>(¶ms)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::INVALID_PARAMS_ERROR
|
|
||||||
})?
|
|
||||||
.implementation(
|
|
||||||
ctx,
|
|
||||||
ParentInfo {
|
|
||||||
method: parent_method,
|
|
||||||
params: parent_params,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(|e| e.into())?,
|
|
||||||
)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::PARSE_ERROR
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map(|f| {
|
|
||||||
f.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::INTERNAL_ERROR
|
|
||||||
})?
|
|
||||||
})
|
|
||||||
.boxed()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Arc::new(|ctx, parent_method, params| {
|
|
||||||
async move {
|
|
||||||
let parent_params = extract::<Cmd::Parent>(¶ms)?;
|
|
||||||
imbl_value::to_value(
|
|
||||||
&extract::<Cmd>(¶ms)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::INVALID_PARAMS_ERROR
|
|
||||||
})?
|
|
||||||
.implementation(
|
|
||||||
ctx,
|
|
||||||
ParentInfo {
|
|
||||||
method: parent_method,
|
|
||||||
params: parent_params,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(|e| e.into())?,
|
|
||||||
)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::PARSE_ERROR
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
sync_impl: Box::new(|ctx, method, params| {
|
|
||||||
let parent = extract::<Cmd::Parent>(¶ms)?;
|
|
||||||
imbl_value::to_value(
|
|
||||||
&extract::<Cmd>(¶ms)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::INVALID_PARAMS_ERROR
|
|
||||||
})?
|
|
||||||
.implementation(
|
|
||||||
ctx,
|
|
||||||
ParentInfo {
|
|
||||||
method,
|
|
||||||
params: parent,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(|e| e.into())?,
|
|
||||||
)
|
|
||||||
.map_err(|e| RpcError {
|
|
||||||
data: Some(e.to_string().into()),
|
|
||||||
..yajrc::PARSE_ERROR
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<Context: crate::Context> DynCommand<Context> {
|
|
||||||
pub fn from_sync<Cmd: SyncCommand<Context> + FromArgMatches + CommandFactory + Serialize>(
|
|
||||||
contains: Contains<Cmd::Parent>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
name: Cmd::NAME,
|
|
||||||
metadata: Cmd::metadata(),
|
|
||||||
implementation: Some(Implementation::for_sync::<Cmd>(contains)),
|
|
||||||
cli: Some(CliBindings::from_leaf::<Cmd>()),
|
|
||||||
subcommands: Cmd::subcommands(ParentChain::<Cmd>(PhantomData)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_sync_no_cli<Cmd: SyncCommand<Context>>(contains: Contains<Cmd::Parent>) -> Self {
|
|
||||||
Self {
|
|
||||||
name: Cmd::NAME,
|
|
||||||
metadata: Cmd::metadata(),
|
|
||||||
implementation: Some(Implementation::for_sync::<Cmd>(contains)),
|
|
||||||
cli: None,
|
|
||||||
subcommands: Cmd::subcommands(ParentChain::<Cmd>(PhantomData)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -502,8 +502,7 @@ where
|
|||||||
match context
|
match context
|
||||||
.call_remote(
|
.call_remote(
|
||||||
&full_method.join("."),
|
&full_method.join("."),
|
||||||
imbl_value::to_value(&Flat(params, inherited_params))
|
imbl_value::to_value(&raw_params).map_err(parse_error)?,
|
||||||
.map_err(parse_error)?,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user