This commit is contained in:
Aiden McClelland
2023-12-14 19:04:05 -07:00
parent e86cd8275e
commit b4661cab40
4 changed files with 385 additions and 307 deletions

View File

@@ -1,8 +1,110 @@
use std::any::Any; use std::any::{Any, TypeId};
use std::collections::BTreeSet;
use tokio::runtime::Handle; use tokio::runtime::Handle;
use crate::Handler;
pub trait Context: Any + Send + 'static { pub trait Context: Any + Send + 'static {
fn runtime(&self) -> Handle { fn runtime(&self) -> Handle {
Handle::current() Handle::current()
} }
} }
#[allow(private_bounds)]
pub trait IntoContext: sealed::Sealed + Any + Send + Sized + 'static {
fn type_ids_for<H: Handler<Self> + ?Sized>(handler: &H) -> Option<BTreeSet<TypeId>>;
fn inner_type_id(&self) -> TypeId;
fn upcast(self) -> AnyContext;
fn downcast(value: AnyContext) -> Result<Self, AnyContext>;
}
impl<C: Context + Sized> IntoContext for C {
fn type_ids_for<H: Handler<Self> + ?Sized>(handler: &H) -> Option<BTreeSet<TypeId>> {
let mut set = BTreeSet::new();
set.insert(TypeId::of::<C>());
Some(set)
}
fn inner_type_id(&self) -> TypeId {
TypeId::of::<C>()
}
fn upcast(self) -> AnyContext {
AnyContext::new(self)
}
fn downcast(value: AnyContext) -> Result<Self, AnyContext> {
if value.0.type_id() == TypeId::of::<C>() {
unsafe { Ok(value.downcast_unchecked::<C>()) }
} else {
Err(value)
}
}
}
pub enum EitherContext<C1, C2> {
C1(C1),
C2(C2),
}
impl<C1: Context, C2: Context> IntoContext for EitherContext<C1, C2> {
fn type_ids_for<H: Handler<Self> + ?Sized>(handler: &H) -> Option<BTreeSet<TypeId>> {
let mut set = BTreeSet::new();
set.insert(TypeId::of::<C1>());
set.insert(TypeId::of::<C2>());
Some(set)
}
fn inner_type_id(&self) -> TypeId {
match self {
EitherContext::C1(c) => c.type_id(),
EitherContext::C2(c) => c.type_id(),
}
}
fn downcast(value: AnyContext) -> Result<Self, AnyContext> {
if value.inner_type_id() == TypeId::of::<C1>() {
Ok(EitherContext::C1(C1::downcast(value)?))
} else if value.inner_type_id() == TypeId::of::<C2>() {
Ok(EitherContext::C2(C2::downcast(value)?))
} else {
Err(value)
}
}
fn upcast(self) -> AnyContext {
match self {
Self::C1(c) => AnyContext::new(c),
Self::C2(c) => AnyContext::new(c),
}
}
}
pub struct AnyContext(Box<dyn Context>);
impl AnyContext {
pub fn new<C: Context>(value: C) -> Self {
Self(Box::new(value))
}
unsafe fn downcast_unchecked<C: Context>(self) -> C {
unsafe {
let raw: *mut dyn Context = Box::into_raw(self.0);
*Box::from_raw(raw as *mut C)
}
}
}
impl IntoContext for AnyContext {
fn type_ids_for<H: Handler<Self> + ?Sized>(_: &H) -> Option<BTreeSet<TypeId>> {
None
}
fn inner_type_id(&self) -> TypeId {
self.0.type_id()
}
fn downcast(value: AnyContext) -> Result<Self, AnyContext> {
Ok(value)
}
fn upcast(self) -> AnyContext {
self
}
}
mod sealed {
pub(crate) trait Sealed {}
impl<C: super::Context> Sealed for C {}
impl<C1: super::Context, C2: super::Context> Sealed for super::EitherContext<C1, C2> {}
impl Sealed for super::AnyContext {}
}

View File

@@ -1,5 +1,7 @@
use std::collections::{BTreeMap, VecDeque}; use std::any::TypeId;
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc;
use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser};
use imbl_value::Value; use imbl_value::Value;
@@ -7,16 +9,18 @@ use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yajrc::RpcError; use yajrc::RpcError;
use crate::context::{AnyContext, IntoContext};
use crate::handler;
use crate::util::{combine, internal_error, invalid_params, Flat}; use crate::util::{combine, internal_error, invalid_params, Flat};
struct HandleAnyArgs { struct HandleAnyArgs {
context: Box<dyn crate::Context>, context: AnyContext,
parent_method: Vec<&'static str>, parent_method: Vec<&'static str>,
method: VecDeque<&'static str>, method: VecDeque<&'static str>,
params: Value, params: Value,
} }
impl HandleAnyArgs { impl HandleAnyArgs {
fn downcast<Context: crate::Context, H>(self) -> Result<HandleArgs<Context, H>, imbl_value::Error> fn downcast<Context: IntoContext, H>(self) -> Result<HandleArgs<Context, H>, imbl_value::Error>
where where
H: Handler<Context>, H: Handler<Context>,
H::Params: DeserializeOwned, H::Params: DeserializeOwned,
@@ -29,38 +33,34 @@ impl HandleAnyArgs {
params, params,
} = self; } = self;
Ok(HandleArgs { Ok(HandleArgs {
context, context: Context::downcast(context).map_err(|_| imbl_value::Error {
kind: imbl_value::ErrorKind::Deserialization,
source: serde::ser::Error::custom("context does not match expected"),
})?,
parent_method, parent_method,
method, method,
params: imbl_value::from_value(params.clone())?, params: imbl_value::from_value(params.clone())?,
inherited_params: imbl_value::from_value(params.clone())?, inherited_params: imbl_value::from_value(params.clone())?,
}) })
} }
}rams.clone())?,
})
}
} }
#[async_trait::async_trait] #[async_trait::async_trait]
trait HandleAny<Context: crate::Context> { trait HandleAny {
fn handle_sync(&self, handle_args: HandleAnyArgs<Context>) -> Result<Value, RpcError>; fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result<Value, RpcError>;
// async fn handle_async(&self, handle_args: HandleAnyArgs<Context>) -> Result<Value, RpcError>; // async fn handle_async(&self, handle_args: HandleAnyArgs<Context>) -> Result<Value, RpcError>;
} }
trait CliBindingsAny<Context: crate::Context> { trait CliBindingsAny {
fn cli_command(&self) -> Command; fn cli_command(&self) -> Command;
fn cli_parse( fn cli_parse(
&self, &self,
matches: &ArgMatches, matches: &ArgMatches,
) -> Result<(VecDeque<&'static str>, Value), clap::Error>; ) -> Result<(VecDeque<&'static str>, Value), clap::Error>;
fn cli_display( fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError>;
&self,
handle_args: HandleAnyArgs<Context>,
result: Value,
) -> Result<(), RpcError>;
} }
pub trait CliBindings<Context: crate::Context>: Handler<Context> { pub trait CliBindings<Context: IntoContext>: Handler<Context> {
fn cli_command(&self) -> Command; fn cli_command(&self) -> Command;
fn cli_parse( fn cli_parse(
&self, &self,
@@ -73,7 +73,7 @@ pub trait CliBindings<Context: crate::Context>: Handler<Context> {
) -> Result<(), Self::Err>; ) -> Result<(), Self::Err>;
} }
pub trait PrintCliResult<Context: crate::Context>: Handler<Context> { pub trait PrintCliResult<Context: IntoContext>: Handler<Context> {
fn print( fn print(
&self, &self,
handle_args: HandleArgs<Context, Self>, handle_args: HandleArgs<Context, Self>,
@@ -83,7 +83,7 @@ pub trait PrintCliResult<Context: crate::Context>: Handler<Context> {
// impl<Context, H> PrintCliResult<Context> for H // impl<Context, H> PrintCliResult<Context> for H
// where // where
// Context: crate::Context, // Context: IntoContext,
// H: Handler<Context>, // H: Handler<Context>,
// H::Ok: Display, // H::Ok: Display,
// { // {
@@ -103,7 +103,7 @@ struct WithCliBindings<Context, H> {
impl<Context, H> Handler<Context> for WithCliBindings<Context, H> impl<Context, H> Handler<Context> for WithCliBindings<Context, H>
where where
Context: crate::Context, Context: IntoContext,
H: Handler<Context>, H: Handler<Context>,
{ {
type Params = H::Params; type Params = H::Params;
@@ -132,7 +132,7 @@ where
impl<Context, H> CliBindings<Context> for WithCliBindings<Context, H> impl<Context, H> CliBindings<Context> for WithCliBindings<Context, H>
where where
Context: crate::Context, Context: IntoContext,
H: Handler<Context>, H: Handler<Context>,
H::Params: FromArgMatches + CommandFactory + Serialize, H::Params: FromArgMatches + CommandFactory + Serialize,
H: PrintCliResult<Context>, H: PrintCliResult<Context>,
@@ -176,18 +176,16 @@ where
} }
} }
trait HandleAnyWithCli<Context: crate::Context>: HandleAny<Context> + CliBindingsAny<Context> {} trait HandleAnyWithCli: HandleAny + CliBindingsAny {}
impl<Context: crate::Context, T: HandleAny<Context> + CliBindingsAny<Context>> impl<T: HandleAny + CliBindingsAny> HandleAnyWithCli for T {}
HandleAnyWithCli<Context> for T
{
}
enum DynHandler<Context> { #[derive(Clone)]
WithoutCli(Box<dyn HandleAny<Context>>), enum DynHandler {
WithCli(Box<dyn HandleAnyWithCli<Context>>), WithoutCli(Arc<dyn HandleAny>),
WithCli(Arc<dyn HandleAnyWithCli>),
} }
impl<Context: crate::Context> HandleAny<Context> for DynHandler<Context> { impl HandleAny for DynHandler {
fn handle_sync(&self, handle_args: HandleAnyArgs<Context>) -> Result<Value, RpcError> { fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result<Value, RpcError> {
match self { match self {
DynHandler::WithoutCli(h) => h.handle_sync(handle_args), DynHandler::WithoutCli(h) => h.handle_sync(handle_args),
DynHandler::WithCli(h) => h.handle_sync(handle_args), DynHandler::WithCli(h) => h.handle_sync(handle_args),
@@ -195,47 +193,23 @@ impl<Context: crate::Context> HandleAny<Context> for DynHandler<Context> {
} }
} }
pub struct HandleArgs<Context: crate::Context, H: Handler<Context> + ?Sized> { pub struct HandleArgs<Context: IntoContext, H: Handler<Context> + ?Sized> {
context: Context, context: Context,
parent_method: Vec<&'static str>, parent_method: Vec<&'static str>,
method: VecDeque<&'static str>, method: VecDeque<&'static str>,
params: H::Params, params: H::Params,
inherited_params: H::InheritedParams, inherited_params: H::InheritedParams,
} }
impl<Context, H> HandleArgs<Context, H>
where
Context: crate::Context,
H: Handler<Context>,
H::Params: Serialize,
H::InheritedParams: Serialize,
{
fn upcast(
Self {
context,
parent_method,
method,
params,
inherited_params,
}: Self,
) -> Result<HandleAnyArgs<Context>, imbl_value::Error> {
Ok(HandleAnyArgs {
context,
parent_method,
method,
params: combine(
imbl_value::to_value(&params)?,
imbl_value::to_value(&inherited_params)?,
)?,
})
}
}
pub trait Handler<Context: crate::Context> { pub trait Handler<Context: IntoContext> {
type Params; type Params;
type InheritedParams; type InheritedParams;
type Ok; type Ok;
type Err; type Err;
fn handle_sync(&self, handle_args: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err>; fn handle_sync(&self, handle_args: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err>;
fn contexts(&self) -> Option<BTreeSet<TypeId>> {
Context::type_ids_for(self)
}
} }
struct AnyHandler<Context, H> { struct AnyHandler<Context, H> {
@@ -243,14 +217,14 @@ struct AnyHandler<Context, H> {
handler: H, handler: H,
} }
impl<Context: crate::Context, H: Handler<Context>> HandleAny<Context> for AnyHandler<Context, H> impl<Context: IntoContext, H: Handler<Context>> HandleAny for AnyHandler<Context, H>
where where
H::Params: DeserializeOwned, H::Params: DeserializeOwned,
H::InheritedParams: DeserializeOwned, H::InheritedParams: DeserializeOwned,
H::Ok: Serialize, H::Ok: Serialize,
RpcError: From<H::Err>, RpcError: From<H::Err>,
{ {
fn handle_sync(&self, handle_args: HandleAnyArgs<Context>) -> Result<Value, RpcError> { fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result<Value, RpcError> {
imbl_value::to_value( imbl_value::to_value(
&self &self
.handler .handler
@@ -260,8 +234,7 @@ where
} }
} }
impl<Context: crate::Context, H: CliBindings<Context>> CliBindingsAny<Context> impl<Context: IntoContext, H: CliBindings<Context>> CliBindingsAny for AnyHandler<Context, H>
for AnyHandler<Context, H>
where where
H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned,
H::InheritedParams: DeserializeOwned, H::InheritedParams: DeserializeOwned,
@@ -277,11 +250,7 @@ where
) -> Result<(VecDeque<&'static str>, Value), clap::Error> { ) -> Result<(VecDeque<&'static str>, Value), clap::Error> {
self.handler.cli_parse(matches) self.handler.cli_parse(matches)
} }
fn cli_display( fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError> {
&self,
handle_args: HandleAnyArgs<Context>,
result: Value,
) -> Result<(), RpcError> {
self.handler self.handler
.cli_display( .cli_display(
handle_args.downcast().map_err(invalid_params)?, handle_args.downcast().map_err(invalid_params)?,
@@ -295,83 +264,83 @@ where
pub struct NoParams {} pub struct NoParams {}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
enum Never {} pub enum Never {}
pub(crate) struct EmptyHandler<Params = NoParams, InheritedParams = NoParams>( #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
PhantomData<(Params, InheritedParams)>, struct Name(Option<&'static str>);
); impl<'a> std::borrow::Borrow<Option<&'a str>> for Name {
impl<Context: crate::Context, Params, InheritedParams> Handler<Context> fn borrow(&self) -> &Option<&'a str> {
for EmptyHandler<Params, InheritedParams> &self.0
{
type Params = Params;
type InheritedParams = InheritedParams;
type Ok = Never;
type Err = RpcError;
fn handle_sync(&self, _: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err> {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
} }
} }
pub struct ParentHandler<Context: crate::Context, H: Handler<Context> = EmptyHandler> { struct SubcommandMap(BTreeMap<Name, BTreeMap<Option<TypeId>, DynHandler>>);
handler: H, impl SubcommandMap {
subcommands: BTreeMap<&'static str, DynHandler<Context>>, fn insert(
&mut self,
ctx_tys: Option<BTreeSet<TypeId>>,
name: Option<&'static str>,
handler: DynHandler,
) {
let mut for_name = self.0.remove(&name).unwrap_or_default();
if let Some(ctx_tys) = ctx_tys {
for ctx_ty in ctx_tys {
for_name.insert(Some(ctx_ty), handler.clone());
}
} else {
for_name.insert(None, handler);
}
self.0.insert(Name(name), for_name);
}
fn get<'a>(&'a self, ctx_ty: TypeId, name: Option<&str>) -> Option<(Name, &'a DynHandler)> {
if let Some((name, for_name)) = self.0.get_key_value(&name) {
if let Some(for_ctx) = for_name.get(&Some(ctx_ty)) {
Some((*name, for_ctx))
} else {
for_name.get(&None).map(|h| (*name, h))
}
} else {
None
}
}
} }
impl<Context: crate::Context> ParentHandler<Context>
where pub struct ParentHandler<Params = NoParams, InheritedParams = NoParams> {
EmptyHandler: CliBindings<Context>, _phantom: PhantomData<(Params, InheritedParams)>,
{ subcommands: SubcommandMap,
}
impl<Params, InheritedParams> ParentHandler<Params, InheritedParams> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
handler: WithCliBindings { _phantom: PhantomData,
_ctx: PhantomData, subcommands: SubcommandMap(BTreeMap::new()),
handler: EmptyHandler(PhantomData).into(),
},
subcommands: BTreeMap::new(),
}
}
}
impl<Context: crate::Context, Params, InheritedParams>
ParentHandler<Context, EmptyHandler<Params, InheritedParams>>
{
pub fn new_no_cli() -> Self {
Self {
handler: EmptyHandler(PhantomData).into(),
subcommands: BTreeMap::new(),
}
}
}
impl<Context: crate::Context, H: Handler<Context>> From<H> for ParentHandler<Context, H> {
fn from(value: H) -> Self {
Self {
handler: value.into(),
subcommands: BTreeMap::new(),
} }
} }
} }
struct InheritanceHandler< struct InheritanceHandler<
Context: crate::Context, Context: IntoContext,
Params,
InheritedParams,
H: Handler<Context>, H: Handler<Context>,
SubH: Handler<Context>, F: Fn(Params, InheritedParams) -> H::InheritedParams,
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams,
> { > {
_phantom: PhantomData<(Context, H)>, _phantom: PhantomData<(Context, Params, InheritedParams)>,
handler: SubH, handler: H,
inherit: F, inherit: F,
} }
impl< impl<Context, Params, InheritedParams, H, F> Handler<Context>
Context: crate::Context, for InheritanceHandler<Context, Params, InheritedParams, H, F>
H: Handler<Context>, where
SubH: Handler<Context>, Context: IntoContext,
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams, H: Handler<Context>,
> Handler<Context> for InheritanceHandler<Context, H, SubH, F> F: Fn(Params, InheritedParams) -> H::InheritedParams,
{ {
type Params = SubH::Params; type Params = H::Params;
type InheritedParams = Flat<H::Params, H::InheritedParams>; type InheritedParams = Flat<Params, InheritedParams>;
type Ok = SubH::Ok; type Ok = H::Ok;
type Err = SubH::Err; type Err = H::Err;
fn handle_sync( fn handle_sync(
&self, &self,
HandleArgs { HandleArgs {
@@ -392,12 +361,12 @@ impl<
} }
} }
impl<Context, H, SubH, F> PrintCliResult<Context> for InheritanceHandler<Context, H, SubH, F> impl<Context, Params, InheritedParams, H, F> PrintCliResult<Context>
for InheritanceHandler<Context, Params, InheritedParams, H, F>
where where
Context: crate::Context, Context: IntoContext,
H: Handler<Context>, H: Handler<Context> + PrintCliResult<Context>,
SubH: Handler<Context> + PrintCliResult<Context>, F: Fn(Params, InheritedParams) -> H::InheritedParams,
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams,
{ {
fn print( fn print(
&self, &self,
@@ -423,17 +392,19 @@ where
} }
} }
impl<Context: crate::Context, H: Handler<Context>> ParentHandler<Context, H> { impl<Params, InheritedParams> ParentHandler<Params, InheritedParams> {
pub fn subcommand<SubH>(mut self, method: &'static str, handler: SubH) -> Self pub fn subcommand<Context, H>(mut self, name: Option<&'static str>, handler: H) -> Self
where where
SubH: Handler<Context, InheritedParams = NoParams> + PrintCliResult<Context> + 'static, Context: IntoContext,
SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, H: Handler<Context, InheritedParams = NoParams> + PrintCliResult<Context> + 'static,
SubH::Ok: Serialize + DeserializeOwned, H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned,
RpcError: From<SubH::Err>, H::Ok: Serialize + DeserializeOwned,
RpcError: From<H::Err>,
{ {
self.subcommands.insert( self.subcommands.insert(
method, handler.contexts(),
DynHandler::WithCli(Box::new(AnyHandler { name,
DynHandler::WithCli(Arc::new(AnyHandler {
_ctx: PhantomData, _ctx: PhantomData,
handler: WithCliBindings { handler: WithCliBindings {
_ctx: PhantomData, _ctx: PhantomData,
@@ -443,29 +414,52 @@ impl<Context: crate::Context, H: Handler<Context>> ParentHandler<Context, H> {
); );
self self
} }
pub fn subcommand_with_inherited<SubH, F>( pub fn subcommand_no_cli<Context, H>(mut self, name: Option<&'static str>, handler: H) -> Self
where
Context: IntoContext,
H: Handler<Context, InheritedParams = NoParams> + 'static,
H::Params: DeserializeOwned,
H::Ok: Serialize,
RpcError: From<H::Err>,
{
self.subcommands.insert(
handler.contexts(),
name,
DynHandler::WithoutCli(Arc::new(AnyHandler {
_ctx: PhantomData,
handler,
})),
);
self
}
}
impl<Params, InheritedParams> ParentHandler<Params, InheritedParams>
where
Params: DeserializeOwned + 'static,
InheritedParams: DeserializeOwned + 'static,
{
pub fn subcommand_with_inherited<Context, H, F>(
mut self, mut self,
method: &'static str, name: Option<&'static str>,
handler: SubH, handler: H,
inherit: F, inherit: F,
) -> Self ) -> Self
where where
SubH: Handler<Context> + PrintCliResult<Context> + 'static, Context: IntoContext,
SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, H: Handler<Context> + PrintCliResult<Context> + 'static,
SubH::Ok: Serialize + DeserializeOwned, H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned,
H: 'static, H::Ok: Serialize + DeserializeOwned,
H::Params: DeserializeOwned, RpcError: From<H::Err>,
H::InheritedParams: DeserializeOwned, F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static,
RpcError: From<SubH::Err>,
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static,
{ {
self.subcommands.insert( self.subcommands.insert(
method, handler.contexts(),
DynHandler::WithCli(Box::new(AnyHandler { name,
DynHandler::WithCli(Arc::new(AnyHandler {
_ctx: PhantomData, _ctx: PhantomData,
handler: WithCliBindings { handler: WithCliBindings {
_ctx: PhantomData, _ctx: PhantomData,
handler: InheritanceHandler::<Context, H, SubH, F> { handler: InheritanceHandler::<Context, Params, InheritedParams, H, F> {
_phantom: PhantomData, _phantom: PhantomData,
handler, handler,
inherit, inherit,
@@ -475,43 +469,26 @@ impl<Context: crate::Context, H: Handler<Context>> ParentHandler<Context, H> {
); );
self self
} }
pub fn subcommand_no_cli<SubH>(mut self, method: &'static str, handler: SubH) -> Self pub fn subcommand_with_inherited_no_cli<Context, H, F>(
where
SubH: Handler<Context, InheritedParams = NoParams> + 'static,
SubH::Params: DeserializeOwned,
SubH::Ok: Serialize,
RpcError: From<SubH::Err>,
{
self.subcommands.insert(
method,
DynHandler::WithoutCli(Box::new(AnyHandler {
_ctx: PhantomData,
handler,
})),
);
self
}
pub fn subcommand_with_inherited_no_cli<SubH, F>(
mut self, mut self,
method: &'static str, name: Option<&'static str>,
handler: SubH, handler: H,
inherit: F, inherit: F,
) -> Self ) -> Self
where where
SubH: Handler<Context> + 'static, Context: IntoContext,
SubH::Params: DeserializeOwned, H: Handler<Context> + 'static,
SubH::Ok: Serialize,
H: 'static,
H::Params: DeserializeOwned, H::Params: DeserializeOwned,
H::InheritedParams: DeserializeOwned, H::Ok: Serialize,
RpcError: From<SubH::Err>, RpcError: From<H::Err>,
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static, F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static,
{ {
self.subcommands.insert( self.subcommands.insert(
method, handler.contexts(),
DynHandler::WithoutCli(Box::new(AnyHandler { name,
DynHandler::WithoutCli(Arc::new(AnyHandler {
_ctx: PhantomData, _ctx: PhantomData,
handler: InheritanceHandler::<Context, H, SubH, F> { handler: InheritanceHandler::<Context, Params, InheritedParams, H, F> {
_phantom: PhantomData, _phantom: PhantomData,
handler, handler,
inherit, inherit,
@@ -522,17 +499,11 @@ impl<Context: crate::Context, H: Handler<Context>> ParentHandler<Context, H> {
} }
} }
impl<Context, H> Handler<Context> for ParentHandler<Context, H> impl<Params: Serialize, InheritedParams: Serialize> Handler<AnyContext>
where for ParentHandler<Params, InheritedParams>
Context: crate::Context,
H: Handler<Context>,
H::Params: Serialize,
H::InheritedParams: Serialize,
H::Ok: Serialize + DeserializeOwned,
RpcError: From<H::Err>,
{ {
type Params = H::Params; type Params = Params;
type InheritedParams = H::InheritedParams; type InheritedParams = InheritedParams;
type Ok = Value; type Ok = Value;
type Err = RpcError; type Err = RpcError;
fn handle_sync( fn handle_sync(
@@ -543,71 +514,74 @@ where
mut method, mut method,
params, params,
inherited_params, inherited_params,
}: HandleArgs<Context, Self>, }: HandleArgs<AnyContext, Self>,
) -> Result<Self::Ok, Self::Err> { ) -> Result<Self::Ok, Self::Err> {
if let Some(cmd) = method.pop_front() { let cmd = method.pop_front();
if let Some(cmd) = cmd {
parent_method.push(cmd); parent_method.push(cmd);
if let Some(sub_handler) = self.subcommands.get(cmd) {
sub_handler.handle_sync(HandleAnyArgs {
context,
parent_method,
method,
params: imbl_value::to_value(&Flat(params, inherited_params))
.map_err(invalid_params)?,
})
} else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
} else {
self.handler
.handle_sync(HandleArgs {
context,
parent_method,
method,
params,
inherited_params,
})
.map_err(RpcError::from)
.and_then(|r| imbl_value::to_value(&r).map_err(internal_error))
} }
if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) {
sub_handler.handle_sync(HandleAnyArgs {
context: context.upcast(),
parent_method,
method,
params: imbl_value::to_value(&Flat(params, inherited_params))
.map_err(invalid_params)?,
})
} else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
}
fn contexts(&self) -> Option<BTreeSet<TypeId>> {
let mut set = BTreeSet::new();
for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) {
set.insert((*ctx_ty)?);
}
Some(set)
} }
} }
impl<Context, H> CliBindings<Context> for ParentHandler<Context, H> impl<Params, InheritedParams> CliBindings<AnyContext> for ParentHandler<Params, InheritedParams>
where where
Context: crate::Context, Params: FromArgMatches + CommandFactory + Serialize,
H: CliBindings<Context>, InheritedParams: Serialize,
H::Params: FromArgMatches + CommandFactory + Serialize,
H::InheritedParams: Serialize,
H::Ok: PrintCliResult<Context> + Serialize + DeserializeOwned,
RpcError: From<H::Err>,
{ {
fn cli_command(&self) -> Command { fn cli_command(&self) -> Command {
H::Params::command().subcommands(self.subcommands.iter().filter_map(|(method, handler)| { // Params::command().subcommands(self.subcommands.0.iter().filter_map(|(name, handlers)| {
match handler { // handlers.iter().find_map(|(ctx_ty, handler)| {
DynHandler::WithCli(h) => Some(h.cli_command().name(method)), // if let DynHandler::WithCli(h) = handler {
DynHandler::WithoutCli(_) => None, // h.cli_command()
} // }
})) // })
// }))
todo!()
} }
fn cli_parse( fn cli_parse(
&self, &self,
matches: &ArgMatches, matches: &ArgMatches,
) -> Result<(VecDeque<&'static str>, Value), clap::Error> { ) -> Result<(VecDeque<&'static str>, Value), clap::Error> {
let (_, root_params) = self.handler.cli_parse(matches)?; // let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?)
if let Some((sub, matches)) = matches.subcommand() { // .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?;
if let Some((sub, DynHandler::WithCli(h))) = self.subcommands.get_key_value(sub) { // let (m, matches) = match matches.subcommand() {
let (mut method, params) = h.cli_parse(matches)?; // Some((m, matches)) => (Some(m), matches),
method.push_front(*sub); // None => (None, matches),
return Ok(( // };
method, // if let Some((SubcommandKey((_, m)), DynHandler::WithCli(h))) = self
combine(root_params, params).map_err(|e| { // .subcommands
clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e) // .get_key_value(&(TypeId::of::<Context>(), m))
})?, // {
)); // let (mut method, params) = h.cli_parse(matches)?;
} // if let Some(m) = m {
} // method.push_front(*m);
Ok((VecDeque::new(), root_params)) // }
// return Ok((
// method,
// combine(root_params, params)
// .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?,
// ));
// }
// Ok((VecDeque::new(), root_params))
todo!()
} }
fn cli_display( fn cli_display(
&self, &self,
@@ -617,39 +591,30 @@ where
mut method, mut method,
params, params,
inherited_params, inherited_params,
}: HandleArgs<Context, Self>, }: HandleArgs<AnyContext, Self>,
result: Self::Ok, result: Self::Ok,
) -> Result<(), Self::Err> { ) -> Result<(), Self::Err> {
if let Some(cmd) = method.pop_front() { // let cmd = method.pop_front();
parent_method.push(cmd); // if let Some(cmd) = cmd {
if let Some(DynHandler::WithCli(sub_handler)) = self.subcommands.get(cmd) { // parent_method.push(cmd);
sub_handler.cli_display( // }
HandleAnyArgs { // if let Some(DynHandler::WithCli(sub_handler)) =
context, // self.subcommands.get(&(context.inner_type_id(), cmd))
parent_method, // {
method, // sub_handler.cli_display(
params: imbl_value::to_value(&Flat(params, inherited_params)) // HandleAnyArgs {
.map_err(invalid_params)?, // context: AnyContext::new(context),
}, // parent_method,
result, // method,
) // params: imbl_value::to_value(&Flat(params, inherited_params))
} else { // .map_err(invalid_params)?,
Err(yajrc::METHOD_NOT_FOUND_ERROR) // },
} // result,
} else { // )
self.handler // } else {
.cli_display( // Err(yajrc::METHOD_NOT_FOUND_ERROR)
HandleArgs { // }
context, todo!()
parent_method,
method,
params,
inherited_params,
},
imbl_value::from_value(result).map_err(internal_error)?,
)
.map_err(RpcError::from)
}
} }
} }
@@ -667,7 +632,7 @@ pub fn from_fn<F, T, E, Args>(function: F) -> FromFn<F, T, E, Args> {
impl<Context, F, T, E> Handler<Context> for FromFn<F, T, E, ()> impl<Context, F, T, E> Handler<Context> for FromFn<F, T, E, ()>
where where
Context: crate::Context, Context: IntoContext,
F: Fn() -> Result<T, E>, F: Fn() -> Result<T, E>,
{ {
type Params = NoParams; type Params = NoParams;
@@ -681,7 +646,7 @@ where
impl<Context, F, T, E> Handler<Context> for FromFn<F, T, E, (Context,)> impl<Context, F, T, E> Handler<Context> for FromFn<F, T, E, (Context,)>
where where
Context: crate::Context, Context: IntoContext,
F: Fn(Context) -> Result<T, E>, F: Fn(Context) -> Result<T, E>,
{ {
type Params = NoParams; type Params = NoParams;
@@ -694,7 +659,7 @@ where
} }
impl<Context, F, T, E, Params> Handler<Context> for FromFn<F, T, E, (Context, Params)> impl<Context, F, T, E, Params> Handler<Context> for FromFn<F, T, E, (Context, Params)>
where where
Context: crate::Context, Context: IntoContext,
F: Fn(Context, Params) -> Result<T, E>, F: Fn(Context, Params) -> Result<T, E>,
Params: DeserializeOwned, Params: DeserializeOwned,
{ {
@@ -712,7 +677,7 @@ where
impl<Context, F, T, E, Params, InheritedParams> Handler<Context> impl<Context, F, T, E, Params, InheritedParams> Handler<Context>
for FromFn<F, T, E, (Context, Params, InheritedParams)> for FromFn<F, T, E, (Context, Params, InheritedParams)>
where where
Context: crate::Context, Context: IntoContext,
F: Fn(Context, Params, InheritedParams) -> Result<T, E>, F: Fn(Context, Params, InheritedParams) -> Result<T, E>,
Params: DeserializeOwned, Params: DeserializeOwned,
InheritedParams: DeserializeOwned, InheritedParams: DeserializeOwned,

View File

@@ -1,6 +1,6 @@
// pub use cli::*; // pub use cli::*;
// pub use command::*; // pub use command::*;
pub use context::Context; pub use context::*;
pub use handler::*; pub use handler::*;
/// `#[command(...)]` /// `#[command(...)]`
/// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only) /// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only)

View File

@@ -1,15 +1,16 @@
use std::fmt::Display;
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use clap::Parser; use clap::Parser;
use rpc_toolkit::{Context, ParentHandler}; use rpc_toolkit::{from_fn, Context, ParentHandler};
use tokio::{ use serde::Deserialize;
runtime::{Handle, Runtime}, use tokio::runtime::{Handle, Runtime};
sync::OnceCell, use tokio::sync::OnceCell;
};
use url::Url; use url::Url;
use yajrc::RpcError; use yajrc::RpcError;
#[derive(Parser)] #[derive(Parser, Deserialize)]
#[command( #[command(
name = "test-cli", name = "test-cli",
version, version,
@@ -17,13 +18,13 @@ use yajrc::RpcError;
about = "This is a test cli application." about = "This is a test cli application."
)] )]
struct CliConfig { struct CliConfig {
host: Option<Url>, host: Option<String>,
config: Option<PathBuf>, config: Option<PathBuf>,
} }
impl CliConfig { impl CliConfig {
fn load_rec(&mut self) -> Result<(), RpcError> { fn load_rec(&mut self) -> Result<(), RpcError> {
if let Some(path) = self.config.as_ref() { if let Some(path) = self.config.as_ref() {
let extra_cfg = let mut extra_cfg: Self =
serde_json::from_str(&std::fs::read_to_string(path).map_err(internal_error)?) serde_json::from_str(&std::fs::read_to_string(path).map_err(internal_error)?)
.map_err(internal_error)?; .map_err(internal_error)?;
extra_cfg.load_rec()?; extra_cfg.load_rec()?;
@@ -46,27 +47,37 @@ struct CliContextSeed {
struct CliContext(Arc<CliContextSeed>); struct CliContext(Arc<CliContextSeed>);
impl Context for CliContext { impl Context for CliContext {
fn runtime(&self) -> Handle { fn runtime(&self) -> Handle {
if self.rt.get().is_none() { if self.0.rt.get().is_none() {
self.rt.set(Runtime::new().unwrap()).unwrap(); self.0.rt.set(Runtime::new().unwrap()).unwrap();
} }
self.rt.get().unwrap().handle() self.0.rt.get().unwrap().handle().clone()
} }
} }
fn make_cli() -> CliApp<CliConfig> { // fn make_cli() -> CliApp<CliConfig> {
CliApp::new::<_, CliConfig>(|mut config| { // CliApp::new::<_, CliConfig>(|mut config| {
config.load_rec()?; // config.load_rec()?;
Ok(CliContext(Arc::new(CliContextSeed { // Ok(CliContext(Arc::new(CliContextSeed {
host: config // host: config
.host // .host
.unwrap_or_else("http://localhost:8080/rpc".parse().unwrap()), // .unwrap_or_else("http://localhost:8080/rpc".parse().unwrap()),
rt: OnceCell::new(), // rt: OnceCell::new(),
}))) // })))
}) // })
.subcommands(make_api()) // .subcommands(make_api())
.subcommands(ParentHandler::new().subcommand("hello", from_fn(|| Ok("world")))); // .subcommands(ParentHandler::new().subcommand("hello", from_fn(|| Ok("world"))));
} // }
fn make_api() -> ParentHandler<CliContext> { fn make_api() -> ParentHandler<CliContext> {
ParentHandler::new().subcommand("hello", from_fn(|| Ok("world"))) ParentHandler::new().subcommand_no_cli(
Some("hello"),
from_fn(|_: CliContext| Ok::<_, RpcError>("world")),
)
}
pub fn internal_error(e: impl Display) -> RpcError {
RpcError {
data: Some(e.to_string().into()),
..yajrc::INTERNAL_ERROR
}
} }