This commit is contained in:
J H
2023-12-14 12:36:35 -07:00
parent 68c8881c0b
commit e86cd8275e
7 changed files with 239 additions and 126 deletions

View File

@@ -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",

View File

@@ -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,

View File

@@ -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()
} }

View File

@@ -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 {

View File

@@ -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;

View 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")))
}

View File

@@ -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());
} // }