mirror of
https://github.com/Start9Labs/rpc-toolkit.git
synced 2026-03-26 02:11:56 +00:00
wip
This commit is contained in:
@@ -966,7 +966,7 @@ pub fn parse_param_attrs(item: &mut ItemFn) -> Result<Vec<ParamType>> {
|
|||||||
attr.span(),
|
attr.span(),
|
||||||
"`context` and `response` are mutually exclusive",
|
"`context` and `response` are mutually exclusive",
|
||||||
));
|
));
|
||||||
} else if matches!(ty, ParamType::Context(_)) {
|
} else if matches!(ty, ParamType::ParentData(_)) {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
attr.span(),
|
attr.span(),
|
||||||
"`parent_data` and `response` are mutually exclusive",
|
"`parent_data` and `response` are mutually exclusive",
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
use std::ffi::OsString;
|
use std::{ffi::OsString, marker::PhantomData};
|
||||||
|
|
||||||
use clap::{ArgMatches, CommandFactory, FromArgMatches};
|
use clap::{ArgMatches, CommandFactory, FromArgMatches};
|
||||||
use futures::future::BoxFuture;
|
use futures::{future::BoxFuture, never::Never};
|
||||||
use futures::{Future, FutureExt};
|
use futures::{Future, FutureExt};
|
||||||
use imbl_value::Value;
|
use imbl_value::Value;
|
||||||
use reqwest::{Client, Method};
|
use reqwest::{Client, Method};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use yajrc::{Id, RpcError};
|
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::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo};
|
||||||
use crate::util::{combine, internal_error, invalid_params, parse_error};
|
use crate::util::{combine, internal_error, invalid_params, parse_error};
|
||||||
// use crate::{CliBindings, SyncCommand};
|
// use crate::{CliBindings, SyncCommand};
|
||||||
@@ -60,11 +61,47 @@ type RpcResponse<'a> = yajrc::RpcResponse<GenericRpcMethod<'static>>;
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
struct CliApp<Context: crate::Context>(ParentCommand<Context>);
|
struct RootCliHandler<Context: crate::Context, Config: CommandFactory + FromArgMatches>(
|
||||||
|
PhantomData<(Context, Config)>,
|
||||||
|
);
|
||||||
|
impl<Context: crate::Context, Config: CommandFactory + FromArgMatches> Handler<Context>
|
||||||
|
for RootCliHandler<Context, Config>
|
||||||
|
{
|
||||||
|
type Params = NoParams;
|
||||||
|
type InheritedParams = NoParams;
|
||||||
|
type Ok = Never;
|
||||||
|
type Err = RpcError;
|
||||||
|
fn handle_sync(&self, _: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err> {
|
||||||
|
Err(yajrc::METHOD_NOT_FOUND_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Context: crate::Context, Config: CommandFactory + FromArgMatches> CliBindings
|
||||||
|
for RootCliHandler<Context, Config>
|
||||||
|
{
|
||||||
|
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<Context, Self>,
|
||||||
|
result: Self::Ok,
|
||||||
|
) -> Result<(), Self::Err> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CliApp<Context: crate::Context, Config: CommandFactory + FromArgMatches>(
|
||||||
|
ParentCommand<Context, EmptyHandler<Config>>,
|
||||||
|
);
|
||||||
impl<Context: crate::Context> CliApp<Context> {
|
impl<Context: crate::Context> CliApp<Context> {
|
||||||
pub fn new<Cmd: FromArgMatches + CommandFactory + Serialize>(
|
pub fn new(commands: Vec<DynCommand<Context>>) -> Self {
|
||||||
commands: Vec<DynCommand<Context>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
cli: CliBindings::from_parent::<Cmd>(),
|
cli: CliBindings::from_parent::<Cmd>(),
|
||||||
commands,
|
commands,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
use std::any::Any;
|
||||||
use tokio::runtime::Handle;
|
use tokio::runtime::Handle;
|
||||||
|
|
||||||
pub trait Context: Send + 'static {
|
pub trait Context: Any + Send + 'static {
|
||||||
type Metadata: Default + Send + Sync;
|
|
||||||
fn runtime(&self) -> Handle {
|
fn runtime(&self) -> Handle {
|
||||||
Handle::current()
|
Handle::current()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ use yajrc::RpcError;
|
|||||||
|
|
||||||
use crate::util::{combine, internal_error, invalid_params, Flat};
|
use crate::util::{combine, internal_error, invalid_params, Flat};
|
||||||
|
|
||||||
struct HandleAnyArgs<Context> {
|
struct HandleAnyArgs {
|
||||||
context: Context,
|
context: Box<dyn crate::Context>,
|
||||||
parent_method: Vec<&'static str>,
|
parent_method: Vec<&'static str>,
|
||||||
method: VecDeque<&'static str>,
|
method: VecDeque<&'static str>,
|
||||||
params: Value,
|
params: Value,
|
||||||
}
|
}
|
||||||
impl<Context: crate::Context> HandleAnyArgs<Context> {
|
impl HandleAnyArgs {
|
||||||
fn downcast<H>(self) -> Result<HandleArgs<Context, H>, imbl_value::Error>
|
fn downcast<Context: crate::Context, H>(self) -> Result<HandleArgs<Context, H>, imbl_value::Error>
|
||||||
where
|
where
|
||||||
H: Handler<Context>,
|
H: Handler<Context>,
|
||||||
H::Params: DeserializeOwned,
|
H::Params: DeserializeOwned,
|
||||||
@@ -36,6 +36,9 @@ impl<Context: crate::Context> HandleAnyArgs<Context> {
|
|||||||
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]
|
||||||
@@ -294,7 +297,9 @@ pub struct NoParams {}
|
|||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
|
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
|
||||||
enum Never {}
|
enum Never {}
|
||||||
|
|
||||||
struct EmptyHandler<Params, InheritedParams>(PhantomData<(Params, InheritedParams)>);
|
pub(crate) struct EmptyHandler<Params = NoParams, InheritedParams = NoParams>(
|
||||||
|
PhantomData<(Params, InheritedParams)>,
|
||||||
|
);
|
||||||
impl<Context: crate::Context, Params, InheritedParams> Handler<Context>
|
impl<Context: crate::Context, Params, InheritedParams> Handler<Context>
|
||||||
for EmptyHandler<Params, InheritedParams>
|
for EmptyHandler<Params, InheritedParams>
|
||||||
{
|
{
|
||||||
@@ -307,14 +312,13 @@ impl<Context: crate::Context, Params, InheritedParams> Handler<Context>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParentHandler<Context: crate::Context, H: Handler<Context>> {
|
pub struct ParentHandler<Context: crate::Context, H: Handler<Context> = EmptyHandler> {
|
||||||
handler: H,
|
handler: H,
|
||||||
subcommands: BTreeMap<&'static str, DynHandler<Context>>,
|
subcommands: BTreeMap<&'static str, DynHandler<Context>>,
|
||||||
}
|
}
|
||||||
impl<Context: crate::Context, Params, InheritedParams>
|
impl<Context: crate::Context> ParentHandler<Context>
|
||||||
ParentHandler<Context, EmptyHandler<Params, InheritedParams>>
|
|
||||||
where
|
where
|
||||||
EmptyHandler<Params, InheritedParams>: CliBindings<Context>,
|
EmptyHandler: CliBindings<Context>,
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
pub use cli::*;
|
// pub use cli::*;
|
||||||
// pub use command::*;
|
// pub use command::*;
|
||||||
pub use context::Context;
|
pub use context::Context;
|
||||||
pub use handler::*;
|
pub use handler::*;
|
||||||
@@ -24,15 +24,15 @@ pub use handler::*;
|
|||||||
///
|
///
|
||||||
/// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context)
|
/// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context)
|
||||||
pub use rpc_toolkit_macro::command;
|
pub use rpc_toolkit_macro::command;
|
||||||
pub use server::*;
|
// pub use server::*;
|
||||||
pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc};
|
pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc};
|
||||||
|
|
||||||
mod cli;
|
// mod cli;
|
||||||
mod command;
|
// mod command;
|
||||||
mod handler;
|
mod handler;
|
||||||
// pub mod command_helpers;
|
// pub mod command_helpers;
|
||||||
mod context;
|
mod context;
|
||||||
// mod metadata;
|
// mod metadata;
|
||||||
// pub mod rpc_server_helpers;
|
// pub mod rpc_server_helpers;
|
||||||
mod server;
|
// mod server;
|
||||||
mod util;
|
mod util;
|
||||||
|
|||||||
72
rpc-toolkit/tests/handler.rs
Normal file
72
rpc-toolkit/tests/handler.rs
Normal file
@@ -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<Url>,
|
||||||
|
config: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
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<Runtime>,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct CliContext(Arc<CliContextSeed>);
|
||||||
|
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<CliConfig> {
|
||||||
|
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<CliContext> {
|
||||||
|
ParentHandler::new().subcommand("hello", from_fn(|| Ok("world")))
|
||||||
|
}
|
||||||
@@ -1,109 +1,109 @@
|
|||||||
use std::path::PathBuf;
|
// use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
// use clap::Parser;
|
||||||
use futures::Future;
|
// use futures::Future;
|
||||||
use rpc_toolkit::{
|
// use rpc_toolkit::{
|
||||||
AsyncCommand, CliContextSocket, Command, Contains, Context, DynCommand, LeafCommand, NoParent,
|
// AsyncCommand, CliContextSocket, Command, Contains, Context, DynCommand, LeafCommand, NoParent,
|
||||||
ParentCommand, ParentInfo, Server, ShutdownHandle,
|
// ParentCommand, ParentInfo, Server, ShutdownHandle,
|
||||||
};
|
// };
|
||||||
use serde::{Deserialize, Serialize};
|
// use serde::{Deserialize, Serialize};
|
||||||
use tokio::net::UnixStream;
|
// use tokio::net::UnixStream;
|
||||||
use yajrc::RpcError;
|
// use yajrc::RpcError;
|
||||||
|
|
||||||
struct ServerContext;
|
// struct ServerContext;
|
||||||
impl Context for ServerContext {
|
// impl Context for ServerContext {
|
||||||
type Metadata = ();
|
// type Metadata = ();
|
||||||
}
|
// }
|
||||||
|
|
||||||
struct CliContext(PathBuf);
|
// struct CliContext(PathBuf);
|
||||||
impl Context for CliContext {
|
// impl Context for CliContext {
|
||||||
type Metadata = ();
|
// type Metadata = ();
|
||||||
}
|
// }
|
||||||
#[async_trait::async_trait]
|
// #[async_trait::async_trait]
|
||||||
impl CliContextSocket for CliContext {
|
// impl CliContextSocket for CliContext {
|
||||||
type Stream = UnixStream;
|
// type Stream = UnixStream;
|
||||||
async fn connect(&self) -> std::io::Result<Self::Stream> {
|
// async fn connect(&self) -> std::io::Result<Self::Stream> {
|
||||||
UnixStream::connect(&self.0).await
|
// UnixStream::connect(&self.0).await
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
#[async_trait::async_trait]
|
// #[async_trait::async_trait]
|
||||||
impl rpc_toolkit::CliContext for CliContext {
|
// impl rpc_toolkit::CliContext for CliContext {
|
||||||
async fn call_remote(
|
// async fn call_remote(
|
||||||
&self,
|
// &self,
|
||||||
method: &str,
|
// method: &str,
|
||||||
params: imbl_value::Value,
|
// params: imbl_value::Value,
|
||||||
) -> Result<imbl_value::Value, RpcError> {
|
// ) -> Result<imbl_value::Value, RpcError> {
|
||||||
<Self as CliContextSocket>::call_remote(self, method, params).await
|
// <Self as CliContextSocket>::call_remote(self, method, params).await
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
async fn run_server() {
|
// async fn run_server() {
|
||||||
Server::new(
|
// Server::new(
|
||||||
vec![
|
// vec![
|
||||||
DynCommand::from_parent::<Group>(Contains::none()),
|
// DynCommand::from_parent::<Group>(Contains::none()),
|
||||||
DynCommand::from_async::<Thing1>(Contains::none()),
|
// DynCommand::from_async::<Thing1>(Contains::none()),
|
||||||
// DynCommand::from_async::<Thing2>(Contains::none()),
|
// // DynCommand::from_async::<Thing2>(Contains::none()),
|
||||||
// DynCommand::from_sync::<Thing3>(Contains::none()),
|
// // DynCommand::from_sync::<Thing3>(Contains::none()),
|
||||||
// DynCommand::from_sync::<Thing4>(Contains::none()),
|
// // DynCommand::from_sync::<Thing4>(Contains::none()),
|
||||||
],
|
// ],
|
||||||
|| async { Ok(ServerContext) },
|
// || async { Ok(ServerContext) },
|
||||||
)
|
// )
|
||||||
.run_unix("./test.sock", |e| eprintln!("{e}"))
|
// .run_unix("./test.sock", |e| eprintln!("{e}"))
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.1
|
// .1
|
||||||
.await
|
// .await
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
// #[derive(Debug, Deserialize, Serialize, Parser)]
|
||||||
struct Group {
|
// struct Group {
|
||||||
#[arg(short, long)]
|
// #[arg(short, long)]
|
||||||
verbose: bool,
|
// verbose: bool,
|
||||||
}
|
// }
|
||||||
impl Command for Group {
|
// impl Command for Group {
|
||||||
const NAME: &'static str = "group";
|
// const NAME: &'static str = "group";
|
||||||
type Parent = NoParent;
|
// type Parent = NoParent;
|
||||||
}
|
// }
|
||||||
impl<Ctx> ParentCommand<Ctx> for Group
|
// impl<Ctx> ParentCommand<Ctx> for Group
|
||||||
where
|
// where
|
||||||
Ctx: Context,
|
// Ctx: Context,
|
||||||
// SubThing: AsyncCommand<Ctx>,
|
// // SubThing: AsyncCommand<Ctx>,
|
||||||
Thing1: AsyncCommand<Ctx>,
|
// Thing1: AsyncCommand<Ctx>,
|
||||||
{
|
// {
|
||||||
fn subcommands(chain: rpc_toolkit::ParentChain<Self>) -> Vec<DynCommand<Ctx>> {
|
// fn subcommands(chain: rpc_toolkit::ParentChain<Self>) -> Vec<DynCommand<Ctx>> {
|
||||||
vec![
|
// vec![
|
||||||
// DynCommand::from_async::<SubThing>(chain.child()),
|
// // DynCommand::from_async::<SubThing>(chain.child()),
|
||||||
DynCommand::from_async::<Thing1>(Contains::none()),
|
// DynCommand::from_async::<Thing1>(Contains::none()),
|
||||||
]
|
// ]
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
// #[derive(Debug, Deserialize, Serialize, Parser)]
|
||||||
struct Thing1 {
|
// struct Thing1 {
|
||||||
thing: String,
|
// thing: String,
|
||||||
}
|
// }
|
||||||
impl Command for Thing1 {
|
// impl Command for Thing1 {
|
||||||
const NAME: &'static str = "thing1";
|
// const NAME: &'static str = "thing1";
|
||||||
type Parent = NoParent;
|
// type Parent = NoParent;
|
||||||
}
|
// }
|
||||||
impl LeafCommand<ServerContext> for Thing1 {
|
// impl LeafCommand<ServerContext> for Thing1 {
|
||||||
type Ok = String;
|
// type Ok = String;
|
||||||
type Err = RpcError;
|
// type Err = RpcError;
|
||||||
fn display(self, _: ServerContext, _: rpc_toolkit::ParentInfo<Self::Parent>, res: Self::Ok) {
|
// fn display(self, _: ServerContext, _: rpc_toolkit::ParentInfo<Self::Parent>, res: Self::Ok) {
|
||||||
println!("{}", res);
|
// println!("{}", res);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
#[async_trait::async_trait]
|
// #[async_trait::async_trait]
|
||||||
impl AsyncCommand<ServerContext> for Thing1 {
|
// impl AsyncCommand<ServerContext> for Thing1 {
|
||||||
async fn implementation(
|
// async fn implementation(
|
||||||
self,
|
// self,
|
||||||
_: ServerContext,
|
// _: ServerContext,
|
||||||
_: ParentInfo<Self::Parent>,
|
// _: ParentInfo<Self::Parent>,
|
||||||
) -> Result<Self::Ok, Self::Err> {
|
// ) -> Result<Self::Ok, Self::Err> {
|
||||||
Ok(format!("Thing1 is {}", self.thing))
|
// Ok(format!("Thing1 is {}", self.thing))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[tokio::test]
|
// #[tokio::test]
|
||||||
async fn test() {
|
// async fn test() {
|
||||||
let server = tokio::spawn(run_server());
|
// let server = tokio::spawn(run_server());
|
||||||
}
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user