diff --git a/rpc-toolkit-macro-internals/src/command/parse.rs b/rpc-toolkit-macro-internals/src/command/parse.rs index d2bf990..fe45a03 100644 --- a/rpc-toolkit-macro-internals/src/command/parse.rs +++ b/rpc-toolkit-macro-internals/src/command/parse.rs @@ -966,7 +966,7 @@ pub fn parse_param_attrs(item: &mut ItemFn) -> Result> { attr.span(), "`context` and `response` are mutually exclusive", )); - } else if matches!(ty, ParamType::Context(_)) { + } else if matches!(ty, ParamType::ParentData(_)) { return Err(Error::new( attr.span(), "`parent_data` and `response` are mutually exclusive", diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 4502d5b..232002f 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,17 +1,18 @@ -use std::ffi::OsString; +use std::{ffi::OsString, marker::PhantomData}; use clap::{ArgMatches, CommandFactory, FromArgMatches}; -use futures::future::BoxFuture; +use futures::{future::BoxFuture, never::Never}; use futures::{Future, FutureExt}; use imbl_value::Value; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; use serde::Serialize; +use std::marker::PhantomData; use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; use url::Url; use yajrc::{Id, RpcError}; -use crate::command::ParentCommand; +use crate::{command::ParentCommand, CliBindings, EmptyHandler, HandleArgs, Handler, NoParams}; // use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; use crate::util::{combine, internal_error, invalid_params, parse_error}; // use crate::{CliBindings, SyncCommand}; @@ -60,11 +61,47 @@ type RpcResponse<'a> = yajrc::RpcResponse>; // } // } -struct CliApp(ParentCommand); +struct RootCliHandler( + PhantomData<(Context, Config)>, +); +impl Handler + for RootCliHandler +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = Never; + type Err = RpcError; + fn handle_sync(&self, _: HandleArgs) -> Result { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } +} +impl CliBindings + for RootCliHandler +{ + fn cli_command(&self) -> clap::Command { + Config::command() + } + + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(std::collections::VecDeque<&'static str>, Value), clap::Error> { + } + + fn cli_display( + &self, + handle_args: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + todo!() + } +} + +struct CliApp( + ParentCommand>, +); impl CliApp { - pub fn new( - commands: Vec>, - ) -> Self { + pub fn new(commands: Vec>) -> Self { Self { cli: CliBindings::from_parent::(), commands, diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index 2c47228..fd7016e 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,7 +1,7 @@ +use std::any::Any; use tokio::runtime::Handle; -pub trait Context: Send + 'static { - type Metadata: Default + Send + Sync; +pub trait Context: Any + Send + 'static { fn runtime(&self) -> Handle { Handle::current() } diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 91756f3..2131fe6 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -9,14 +9,14 @@ use yajrc::RpcError; use crate::util::{combine, internal_error, invalid_params, Flat}; -struct HandleAnyArgs { - context: Context, +struct HandleAnyArgs { + context: Box, parent_method: Vec<&'static str>, method: VecDeque<&'static str>, params: Value, } -impl HandleAnyArgs { - fn downcast(self) -> Result, imbl_value::Error> +impl HandleAnyArgs { + fn downcast(self) -> Result, imbl_value::Error> where H: Handler, H::Params: DeserializeOwned, @@ -36,6 +36,9 @@ impl HandleAnyArgs { inherited_params: imbl_value::from_value(params.clone())?, }) } +}rams.clone())?, + }) + } } #[async_trait::async_trait] @@ -294,7 +297,9 @@ pub struct NoParams {} #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] enum Never {} -struct EmptyHandler(PhantomData<(Params, InheritedParams)>); +pub(crate) struct EmptyHandler( + PhantomData<(Params, InheritedParams)>, +); impl Handler for EmptyHandler { @@ -307,14 +312,13 @@ impl Handler } } -pub struct ParentHandler> { +pub struct ParentHandler = EmptyHandler> { handler: H, subcommands: BTreeMap<&'static str, DynHandler>, } -impl - ParentHandler> +impl ParentHandler where - EmptyHandler: CliBindings, + EmptyHandler: CliBindings, { pub fn new() -> Self { Self { diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index e69e045..da703a3 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -1,4 +1,4 @@ -pub use cli::*; +// pub use cli::*; // pub use command::*; pub use context::Context; pub use handler::*; @@ -24,15 +24,15 @@ pub use handler::*; /// /// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context) pub use rpc_toolkit_macro::command; -pub use server::*; +// pub use server::*; pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; -mod cli; -mod command; +// mod cli; +// mod command; mod handler; // pub mod command_helpers; mod context; // mod metadata; // pub mod rpc_server_helpers; -mod server; +// mod server; mod util; diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs new file mode 100644 index 0000000..d0c643f --- /dev/null +++ b/rpc-toolkit/tests/handler.rs @@ -0,0 +1,72 @@ +use std::sync::Arc; + +use clap::Parser; +use rpc_toolkit::{Context, ParentHandler}; +use tokio::{ + runtime::{Handle, Runtime}, + sync::OnceCell, +}; +use url::Url; +use yajrc::RpcError; + +#[derive(Parser)] +#[command( + name = "test-cli", + version, + author, + about = "This is a test cli application." +)] +struct CliConfig { + host: Option, + config: Option, +} +impl CliConfig { + fn load_rec(&mut self) -> Result<(), RpcError> { + if let Some(path) = self.config.as_ref() { + let extra_cfg = + serde_json::from_str(&std::fs::read_to_string(path).map_err(internal_error)?) + .map_err(internal_error)?; + extra_cfg.load_rec()?; + self.merge_with(extra_cfg); + } + Ok(()) + } + fn merge_with(&mut self, extra: Self) { + if self.host.is_none() { + self.host = extra.host; + } + } +} + +struct CliContextSeed { + host: Url, + rt: OnceCell, +} +#[derive(Clone)] +struct CliContext(Arc); +impl Context for CliContext { + fn runtime(&self) -> Handle { + if self.rt.get().is_none() { + self.rt.set(Runtime::new().unwrap()).unwrap(); + } + self.rt.get().unwrap().handle() + } +} + +fn make_cli() -> CliApp { + CliApp::new::<_, CliConfig>(|mut config| { + config.load_rec()?; + Ok(CliContext(Arc::new(CliContextSeed { + host: config + .host + .unwrap_or_else("http://localhost:8080/rpc".parse().unwrap()), + rt: OnceCell::new(), + }))) + }) + .subcommands(make_api()) + .subcommands(ParentHandler::new().subcommand("hello", from_fn(|| Ok("world")))); +} + +fn make_api() -> ParentHandler { + ParentHandler::new().subcommand("hello", from_fn(|| Ok("world"))) +} diff --git a/rpc-toolkit/tests/test.rs b/rpc-toolkit/tests/test.rs index 8c8b206..bc555b0 100644 --- a/rpc-toolkit/tests/test.rs +++ b/rpc-toolkit/tests/test.rs @@ -1,109 +1,109 @@ -use std::path::PathBuf; +// use std::path::PathBuf; -use clap::Parser; -use futures::Future; -use rpc_toolkit::{ - AsyncCommand, CliContextSocket, Command, Contains, Context, DynCommand, LeafCommand, NoParent, - ParentCommand, ParentInfo, Server, ShutdownHandle, -}; -use serde::{Deserialize, Serialize}; -use tokio::net::UnixStream; -use yajrc::RpcError; +// use clap::Parser; +// use futures::Future; +// use rpc_toolkit::{ +// AsyncCommand, CliContextSocket, Command, Contains, Context, DynCommand, LeafCommand, NoParent, +// ParentCommand, ParentInfo, Server, ShutdownHandle, +// }; +// use serde::{Deserialize, Serialize}; +// use tokio::net::UnixStream; +// use yajrc::RpcError; -struct ServerContext; -impl Context for ServerContext { - type Metadata = (); -} +// struct ServerContext; +// impl Context for ServerContext { +// type Metadata = (); +// } -struct CliContext(PathBuf); -impl Context for CliContext { - type Metadata = (); -} -#[async_trait::async_trait] -impl CliContextSocket for CliContext { - type Stream = UnixStream; - async fn connect(&self) -> std::io::Result { - UnixStream::connect(&self.0).await - } -} -#[async_trait::async_trait] -impl rpc_toolkit::CliContext for CliContext { - async fn call_remote( - &self, - method: &str, - params: imbl_value::Value, - ) -> Result { - ::call_remote(self, method, params).await - } -} +// struct CliContext(PathBuf); +// impl Context for CliContext { +// type Metadata = (); +// } +// #[async_trait::async_trait] +// impl CliContextSocket for CliContext { +// type Stream = UnixStream; +// async fn connect(&self) -> std::io::Result { +// UnixStream::connect(&self.0).await +// } +// } +// #[async_trait::async_trait] +// impl rpc_toolkit::CliContext for CliContext { +// async fn call_remote( +// &self, +// method: &str, +// params: imbl_value::Value, +// ) -> Result { +// ::call_remote(self, method, params).await +// } +// } -async fn run_server() { - Server::new( - vec![ - DynCommand::from_parent::(Contains::none()), - DynCommand::from_async::(Contains::none()), - // DynCommand::from_async::(Contains::none()), - // DynCommand::from_sync::(Contains::none()), - // DynCommand::from_sync::(Contains::none()), - ], - || async { Ok(ServerContext) }, - ) - .run_unix("./test.sock", |e| eprintln!("{e}")) - .unwrap() - .1 - .await -} +// async fn run_server() { +// Server::new( +// vec![ +// DynCommand::from_parent::(Contains::none()), +// DynCommand::from_async::(Contains::none()), +// // DynCommand::from_async::(Contains::none()), +// // DynCommand::from_sync::(Contains::none()), +// // DynCommand::from_sync::(Contains::none()), +// ], +// || async { Ok(ServerContext) }, +// ) +// .run_unix("./test.sock", |e| eprintln!("{e}")) +// .unwrap() +// .1 +// .await +// } -#[derive(Debug, Deserialize, Serialize, Parser)] -struct Group { - #[arg(short, long)] - verbose: bool, -} -impl Command for Group { - const NAME: &'static str = "group"; - type Parent = NoParent; -} -impl ParentCommand for Group -where - Ctx: Context, - // SubThing: AsyncCommand, - Thing1: AsyncCommand, -{ - fn subcommands(chain: rpc_toolkit::ParentChain) -> Vec> { - vec![ - // DynCommand::from_async::(chain.child()), - DynCommand::from_async::(Contains::none()), - ] - } -} +// #[derive(Debug, Deserialize, Serialize, Parser)] +// struct Group { +// #[arg(short, long)] +// verbose: bool, +// } +// impl Command for Group { +// const NAME: &'static str = "group"; +// type Parent = NoParent; +// } +// impl ParentCommand for Group +// where +// Ctx: Context, +// // SubThing: AsyncCommand, +// Thing1: AsyncCommand, +// { +// fn subcommands(chain: rpc_toolkit::ParentChain) -> Vec> { +// vec![ +// // DynCommand::from_async::(chain.child()), +// DynCommand::from_async::(Contains::none()), +// ] +// } +// } -#[derive(Debug, Deserialize, Serialize, Parser)] -struct Thing1 { - thing: String, -} -impl Command for Thing1 { - const NAME: &'static str = "thing1"; - type Parent = NoParent; -} -impl LeafCommand for Thing1 { - type Ok = String; - type Err = RpcError; - fn display(self, _: ServerContext, _: rpc_toolkit::ParentInfo, res: Self::Ok) { - println!("{}", res); - } -} -#[async_trait::async_trait] -impl AsyncCommand for Thing1 { - async fn implementation( - self, - _: ServerContext, - _: ParentInfo, - ) -> Result { - Ok(format!("Thing1 is {}", self.thing)) - } -} +// #[derive(Debug, Deserialize, Serialize, Parser)] +// struct Thing1 { +// thing: String, +// } +// impl Command for Thing1 { +// const NAME: &'static str = "thing1"; +// type Parent = NoParent; +// } +// impl LeafCommand for Thing1 { +// type Ok = String; +// type Err = RpcError; +// fn display(self, _: ServerContext, _: rpc_toolkit::ParentInfo, res: Self::Ok) { +// println!("{}", res); +// } +// } +// #[async_trait::async_trait] +// impl AsyncCommand for Thing1 { +// async fn implementation( +// self, +// _: ServerContext, +// _: ParentInfo, +// ) -> Result { +// Ok(format!("Thing1 is {}", self.thing)) +// } +// } -#[tokio::test] -async fn test() { - let server = tokio::spawn(run_server()); -} +// #[tokio::test] +// async fn test() { +// let server = tokio::spawn(run_server()); +// }