mirror of
https://github.com/Start9Labs/rpc-toolkit.git
synced 2026-03-26 02:11:56 +00:00
wip
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -690,6 +690,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_format"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e479e99b287d578ed5f6cd4c92cdf48db219088adb9c5b14f7c155b71dfba792"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -992,6 +998,7 @@ dependencies = [
|
||||
"http-body-util",
|
||||
"hyper 1.0.1",
|
||||
"imbl-value",
|
||||
"lazy_format",
|
||||
"lazy_static",
|
||||
"openssl",
|
||||
"reqwest",
|
||||
|
||||
@@ -24,6 +24,7 @@ http = "1"
|
||||
http-body-util = "0.1"
|
||||
hyper = { version = "1", features = ["server", "http1", "http2", "client"] }
|
||||
imbl-value = "0.1"
|
||||
lazy_format = "2"
|
||||
lazy_static = "1.4"
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
reqwest = { version = "0.11" }
|
||||
|
||||
@@ -11,58 +11,56 @@ use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader
|
||||
use url::Url;
|
||||
use yajrc::{Id, RpcError};
|
||||
|
||||
use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo};
|
||||
use crate::command::ParentCommand;
|
||||
// use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo};
|
||||
use crate::util::{combine, internal_error, invalid_params, parse_error};
|
||||
use crate::{CliBindings, SyncCommand};
|
||||
// use crate::{CliBindings, SyncCommand};
|
||||
|
||||
type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>;
|
||||
type RpcRequest<'a> = yajrc::RpcRequest<GenericRpcMethod<'a>>;
|
||||
type RpcResponse<'a> = yajrc::RpcResponse<GenericRpcMethod<'static>>;
|
||||
|
||||
impl<Context: crate::Context> DynCommand<Context> {
|
||||
fn cli_app(&self) -> Option<clap::Command> {
|
||||
if let Some(cli) = &self.cli {
|
||||
Some(
|
||||
cli.cmd
|
||||
.clone()
|
||||
.name(self.name)
|
||||
.subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn cmd_from_cli_matches(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
parent: ParentInfo<Value>,
|
||||
) -> Result<(Vec<&'static str>, Value, &DynCommand<Context>), RpcError> {
|
||||
let params = combine(
|
||||
parent.params,
|
||||
(self
|
||||
.cli
|
||||
.as_ref()
|
||||
.ok_or(yajrc::METHOD_NOT_FOUND_ERROR)?
|
||||
.parser)(matches)?,
|
||||
)?;
|
||||
if let Some((cmd, matches)) = matches.subcommand() {
|
||||
let mut method = parent.method;
|
||||
method.push(self.name);
|
||||
self.subcommands
|
||||
.iter()
|
||||
.find(|c| c.name == cmd)
|
||||
.ok_or(yajrc::METHOD_NOT_FOUND_ERROR)?
|
||||
.cmd_from_cli_matches(matches, ParentInfo { method, params })
|
||||
} else {
|
||||
Ok((parent.method, params, self))
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl<Context: crate::Context> DynCommand<Context> {
|
||||
// fn cli_app(&self) -> Option<clap::Command> {
|
||||
// if let Some(cli) = &self.cli {
|
||||
// Some(
|
||||
// cli.cmd
|
||||
// .clone()
|
||||
// .name(self.name)
|
||||
// .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())),
|
||||
// )
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
// fn cmd_from_cli_matches(
|
||||
// &self,
|
||||
// matches: &ArgMatches,
|
||||
// parent: ParentInfo<Value>,
|
||||
// ) -> Result<(Vec<&'static str>, Value, &DynCommand<Context>), RpcError> {
|
||||
// let params = combine(
|
||||
// parent.params,
|
||||
// (self
|
||||
// .cli
|
||||
// .as_ref()
|
||||
// .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)?
|
||||
// .parser)(matches)?,
|
||||
// )?;
|
||||
// if let Some((cmd, matches)) = matches.subcommand() {
|
||||
// let mut method = parent.method;
|
||||
// method.push(self.name);
|
||||
// self.subcommands
|
||||
// .iter()
|
||||
// .find(|c| c.name == cmd)
|
||||
// .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)?
|
||||
// .cmd_from_cli_matches(matches, ParentInfo { method, params })
|
||||
// } else {
|
||||
// Ok((parent.method, params, self))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
struct CliApp<Context: crate::Context> {
|
||||
cli: CliBindings<Context>,
|
||||
commands: Vec<DynCommand<Context>>,
|
||||
}
|
||||
struct CliApp<Context: crate::Context>(ParentCommand<Context>);
|
||||
impl<Context: crate::Context> CliApp<Context> {
|
||||
pub fn new<Cmd: FromArgMatches + CommandFactory + Serialize>(
|
||||
commands: Vec<DynCommand<Context>>,
|
||||
@@ -306,7 +304,8 @@ where
|
||||
combine(
|
||||
imbl_value::to_value(&self).map_err(invalid_params)?,
|
||||
imbl_value::to_value(&parent.params).map_err(invalid_params)?,
|
||||
)?,
|
||||
)
|
||||
.map_err(invalid_params)?,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -330,15 +329,18 @@ where
|
||||
) -> Result<Self::Ok, Self::Err> {
|
||||
let mut method = parent.method;
|
||||
method.push(Self::NAME);
|
||||
Ok(
|
||||
imbl_value::from_value(ctx.runtime().block_on(ctx.call_remote(
|
||||
&method.join("."),
|
||||
combine(
|
||||
imbl_value::to_value(&self).map_err(invalid_params)?,
|
||||
imbl_value::to_value(&parent.params).map_err(invalid_params)?,
|
||||
)?,
|
||||
))?)
|
||||
.map_err(parse_error)?,
|
||||
Ok(imbl_value::from_value(
|
||||
ctx.runtime().block_on(
|
||||
ctx.call_remote(
|
||||
&method.join("."),
|
||||
combine(
|
||||
imbl_value::to_value(&self).map_err(invalid_params)?,
|
||||
imbl_value::to_value(&parent.params).map_err(invalid_params)?,
|
||||
)
|
||||
.map_err(invalid_params)?,
|
||||
),
|
||||
)?,
|
||||
)
|
||||
.map_err(parse_error)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
use std::fmt::Display;
|
||||
use std::io::Stdin;
|
||||
use std::marker::PhantomData;
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use hyper::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use yajrc::{GenericRpcMethod, Id, RpcError, RpcRequest, RpcResponse};
|
||||
|
||||
use crate::Context;
|
||||
|
||||
pub mod prelude {
|
||||
pub use std::borrow::Cow;
|
||||
pub use std::marker::PhantomData;
|
||||
|
||||
pub use clap::{App, AppSettings, Arg, ArgMatches};
|
||||
pub use hyper::http::request::Parts as RequestParts;
|
||||
pub use hyper::http::response::Parts as ResponseParts;
|
||||
pub use serde::{Deserialize, Serialize};
|
||||
pub use serde_json::{from_value, to_value, Value};
|
||||
pub use tokio::runtime::Runtime;
|
||||
pub use tokio::task::spawn_blocking;
|
||||
pub use yajrc::{self, RpcError};
|
||||
|
||||
pub use super::{
|
||||
call_remote, default_arg_parser, default_display, default_stdin_parser, make_phantom,
|
||||
match_types,
|
||||
};
|
||||
pub use crate::Context;
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RequestError {
|
||||
#[error("JSON Error: {0}")]
|
||||
JSON(#[from] serde_json::Error),
|
||||
#[cfg(feature = "cbor")]
|
||||
#[error("CBOR Error: {0}")]
|
||||
CBOR(#[from] serde_cbor::Error),
|
||||
#[error("HTTP Error: {0}")]
|
||||
HTTP(#[from] reqwest::Error),
|
||||
#[error("Missing Content-Type")]
|
||||
MissingContentType,
|
||||
}
|
||||
|
||||
pub fn make_phantom<T>(_actual: T) -> PhantomData<T> {
|
||||
PhantomData
|
||||
}
|
||||
|
||||
pub fn match_types<T>(_: &T, _: &T) {}
|
||||
|
||||
pub async fn call_remote<Ctx: Context, Params: Serialize, Res: for<'de> Deserialize<'de>>(
|
||||
ctx: Ctx,
|
||||
method: &str,
|
||||
params: Params,
|
||||
_return_ty: PhantomData<Res>,
|
||||
) -> Result<RpcResponse<GenericRpcMethod<&str, Params, Res>>, RequestError> {
|
||||
let rpc_req: RpcRequest<GenericRpcMethod<&str, Params, Res>> = RpcRequest {
|
||||
id: Some(Id::Number(0.into())),
|
||||
method: GenericRpcMethod::new(method),
|
||||
params,
|
||||
};
|
||||
let mut req = ctx.client().request(Method::POST, ctx.url());
|
||||
let body;
|
||||
#[cfg(feature = "cbor")]
|
||||
{
|
||||
req = req.header("content-type", "application/cbor");
|
||||
req = req.header("accept", "application/cbor, application/json");
|
||||
body = serde_cbor::to_vec(&rpc_req)?;
|
||||
}
|
||||
#[cfg(not(feature = "cbor"))]
|
||||
{
|
||||
req = req.header("content-type", "application/json");
|
||||
req = req.header("accept", "application/json");
|
||||
body = serde_json::to_vec(&req)?;
|
||||
}
|
||||
let res = req
|
||||
.header("content-length", body.len())
|
||||
.body(body)
|
||||
.send()
|
||||
.await?;
|
||||
Ok(
|
||||
match res
|
||||
.headers()
|
||||
.get("content-type")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
{
|
||||
Some("application/json") => serde_json::from_slice(&*res.bytes().await?)?,
|
||||
#[cfg(feature = "cbor")]
|
||||
Some("application/cbor") => serde_cbor::from_slice(&*res.bytes().await?)?,
|
||||
_ => return Err(RequestError::MissingContentType),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn default_arg_parser<T: FromStr<Err = E>, E: Display>(
|
||||
arg: &str,
|
||||
_: &ArgMatches,
|
||||
) -> Result<T, RpcError> {
|
||||
arg.parse().map_err(|e| RpcError {
|
||||
data: Some(format!("{}", e).into()),
|
||||
..yajrc::INVALID_PARAMS_ERROR
|
||||
})
|
||||
}
|
||||
|
||||
pub fn default_stdin_parser<T: FromStr<Err = E>, E: Display>(
|
||||
stdin: &mut Stdin,
|
||||
_: &ArgMatches,
|
||||
) -> Result<T, RpcError> {
|
||||
let mut s = String::new();
|
||||
stdin.read_line(&mut s).map_err(|e| RpcError {
|
||||
data: Some(format!("{}", e).into()),
|
||||
..yajrc::INVALID_PARAMS_ERROR
|
||||
})?;
|
||||
if let Some(s) = s.strip_suffix("\n") {
|
||||
s
|
||||
} else {
|
||||
&s
|
||||
}
|
||||
.parse()
|
||||
.map_err(|e| RpcError {
|
||||
data: Some(format!("{}", e).into()),
|
||||
..yajrc::INVALID_PARAMS_ERROR
|
||||
})
|
||||
}
|
||||
|
||||
pub fn default_display<T: Display>(t: T, _: &ArgMatches) {
|
||||
println!("{}", t)
|
||||
}
|
||||
769
rpc-toolkit/src/handler.rs
Normal file
769
rpc-toolkit/src/handler.rs
Normal file
@@ -0,0 +1,769 @@
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser};
|
||||
use imbl_value::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yajrc::RpcError;
|
||||
|
||||
use crate::util::{combine, internal_error, invalid_params, Flat};
|
||||
|
||||
struct HandleAnyArgs<Context> {
|
||||
context: Context,
|
||||
parent_method: Vec<&'static str>,
|
||||
method: VecDeque<&'static str>,
|
||||
params: Value,
|
||||
}
|
||||
impl<Context: crate::Context> HandleAnyArgs<Context> {
|
||||
fn downcast<H>(self) -> Result<HandleArgs<Context, H>, imbl_value::Error>
|
||||
where
|
||||
H: Handler<Context>,
|
||||
H::Params: DeserializeOwned,
|
||||
H::InheritedParams: DeserializeOwned,
|
||||
{
|
||||
let Self {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
} = self;
|
||||
Ok(HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params: imbl_value::from_value(params.clone())?,
|
||||
inherited_params: imbl_value::from_value(params.clone())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
trait HandleAny<Context: crate::Context> {
|
||||
fn handle_sync(&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> {
|
||||
fn cli_command(&self) -> Command;
|
||||
fn cli_parse(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
) -> Result<(VecDeque<&'static str>, Value), clap::Error>;
|
||||
fn cli_display(
|
||||
&self,
|
||||
handle_args: HandleAnyArgs<Context>,
|
||||
result: Value,
|
||||
) -> Result<(), RpcError>;
|
||||
}
|
||||
|
||||
pub trait CliBindings<Context: crate::Context>: Handler<Context> {
|
||||
fn cli_command(&self) -> Command;
|
||||
fn cli_parse(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
) -> Result<(VecDeque<&'static str>, Value), clap::Error>;
|
||||
fn cli_display(
|
||||
&self,
|
||||
handle_args: HandleArgs<Context, Self>,
|
||||
result: Self::Ok,
|
||||
) -> Result<(), Self::Err>;
|
||||
}
|
||||
|
||||
pub trait PrintCliResult<Context: crate::Context>: Handler<Context> {
|
||||
fn print(
|
||||
&self,
|
||||
handle_args: HandleArgs<Context, Self>,
|
||||
result: Self::Ok,
|
||||
) -> Result<(), Self::Err>;
|
||||
}
|
||||
|
||||
// impl<Context, H> PrintCliResult<Context> for H
|
||||
// where
|
||||
// Context: crate::Context,
|
||||
// H: Handler<Context>,
|
||||
// H::Ok: Display,
|
||||
// {
|
||||
// fn print(
|
||||
// &self,
|
||||
// handle_args: HandleArgs<Context, Self>,
|
||||
// result: Self::Ok,
|
||||
// ) -> Result<(), Self::Err> {
|
||||
// Ok(println!("{result}"))
|
||||
// }
|
||||
// }
|
||||
|
||||
struct WithCliBindings<Context, H> {
|
||||
_ctx: PhantomData<Context>,
|
||||
handler: H,
|
||||
}
|
||||
|
||||
impl<Context, H> Handler<Context> for WithCliBindings<Context, H>
|
||||
where
|
||||
Context: crate::Context,
|
||||
H: Handler<Context>,
|
||||
{
|
||||
type Params = H::Params;
|
||||
type InheritedParams = H::InheritedParams;
|
||||
type Ok = H::Ok;
|
||||
type Err = H::Err;
|
||||
fn handle_sync(
|
||||
&self,
|
||||
HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params,
|
||||
}: HandleArgs<Context, Self>,
|
||||
) -> Result<Self::Ok, Self::Err> {
|
||||
self.handler.handle_sync(HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, H> CliBindings<Context> for WithCliBindings<Context, H>
|
||||
where
|
||||
Context: crate::Context,
|
||||
H: Handler<Context>,
|
||||
H::Params: FromArgMatches + CommandFactory + Serialize,
|
||||
H: PrintCliResult<Context>,
|
||||
{
|
||||
fn cli_command(&self) -> Command {
|
||||
H::Params::command()
|
||||
}
|
||||
fn cli_parse(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
) -> Result<(VecDeque<&'static str>, Value), clap::Error> {
|
||||
H::Params::from_arg_matches(matches).and_then(|a| {
|
||||
Ok((
|
||||
VecDeque::new(),
|
||||
imbl_value::to_value(&a)
|
||||
.map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?,
|
||||
))
|
||||
})
|
||||
}
|
||||
fn cli_display(
|
||||
&self,
|
||||
HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params,
|
||||
}: HandleArgs<Context, Self>,
|
||||
result: Self::Ok,
|
||||
) -> Result<(), Self::Err> {
|
||||
self.handler.print(
|
||||
HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params,
|
||||
},
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
trait HandleAnyWithCli<Context: crate::Context>: HandleAny<Context> + CliBindingsAny<Context> {}
|
||||
impl<Context: crate::Context, T: HandleAny<Context> + CliBindingsAny<Context>>
|
||||
HandleAnyWithCli<Context> for T
|
||||
{
|
||||
}
|
||||
|
||||
enum DynHandler<Context> {
|
||||
WithoutCli(Box<dyn HandleAny<Context>>),
|
||||
WithCli(Box<dyn HandleAnyWithCli<Context>>),
|
||||
}
|
||||
impl<Context: crate::Context> HandleAny<Context> for DynHandler<Context> {
|
||||
fn handle_sync(&self, handle_args: HandleAnyArgs<Context>) -> Result<Value, RpcError> {
|
||||
match self {
|
||||
DynHandler::WithoutCli(h) => h.handle_sync(handle_args),
|
||||
DynHandler::WithCli(h) => h.handle_sync(handle_args),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HandleArgs<Context: crate::Context, H: Handler<Context> + ?Sized> {
|
||||
context: Context,
|
||||
parent_method: Vec<&'static str>,
|
||||
method: VecDeque<&'static str>,
|
||||
params: H::Params,
|
||||
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(¶ms)?,
|
||||
imbl_value::to_value(&inherited_params)?,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Handler<Context: crate::Context> {
|
||||
type Params;
|
||||
type InheritedParams;
|
||||
type Ok;
|
||||
type Err;
|
||||
fn handle_sync(&self, handle_args: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err>;
|
||||
}
|
||||
|
||||
struct AnyHandler<Context, H> {
|
||||
_ctx: PhantomData<Context>,
|
||||
handler: H,
|
||||
}
|
||||
|
||||
impl<Context: crate::Context, H: Handler<Context>> HandleAny<Context> for AnyHandler<Context, H>
|
||||
where
|
||||
H::Params: DeserializeOwned,
|
||||
H::InheritedParams: DeserializeOwned,
|
||||
H::Ok: Serialize,
|
||||
RpcError: From<H::Err>,
|
||||
{
|
||||
fn handle_sync(&self, handle_args: HandleAnyArgs<Context>) -> Result<Value, RpcError> {
|
||||
imbl_value::to_value(
|
||||
&self
|
||||
.handler
|
||||
.handle_sync(handle_args.downcast().map_err(invalid_params)?)?,
|
||||
)
|
||||
.map_err(internal_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context: crate::Context, H: CliBindings<Context>> CliBindingsAny<Context>
|
||||
for AnyHandler<Context, H>
|
||||
where
|
||||
H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned,
|
||||
H::InheritedParams: DeserializeOwned,
|
||||
H::Ok: Serialize + DeserializeOwned,
|
||||
RpcError: From<H::Err>,
|
||||
{
|
||||
fn cli_command(&self) -> Command {
|
||||
self.handler.cli_command()
|
||||
}
|
||||
fn cli_parse(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
) -> Result<(VecDeque<&'static str>, Value), clap::Error> {
|
||||
self.handler.cli_parse(matches)
|
||||
}
|
||||
fn cli_display(
|
||||
&self,
|
||||
handle_args: HandleAnyArgs<Context>,
|
||||
result: Value,
|
||||
) -> Result<(), RpcError> {
|
||||
self.handler
|
||||
.cli_display(
|
||||
handle_args.downcast().map_err(invalid_params)?,
|
||||
imbl_value::from_value(result).map_err(internal_error)?,
|
||||
)
|
||||
.map_err(RpcError::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
|
||||
pub struct NoParams {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
|
||||
enum Never {}
|
||||
|
||||
struct EmptyHandler<Params, InheritedParams>(PhantomData<(Params, InheritedParams)>);
|
||||
impl<Context: crate::Context, Params, InheritedParams> Handler<Context>
|
||||
for EmptyHandler<Params, InheritedParams>
|
||||
{
|
||||
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>> {
|
||||
handler: H,
|
||||
subcommands: BTreeMap<&'static str, DynHandler<Context>>,
|
||||
}
|
||||
impl<Context: crate::Context, Params, InheritedParams>
|
||||
ParentHandler<Context, EmptyHandler<Params, InheritedParams>>
|
||||
where
|
||||
EmptyHandler<Params, InheritedParams>: CliBindings<Context>,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
handler: WithCliBindings {
|
||||
_ctx: PhantomData,
|
||||
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<
|
||||
Context: crate::Context,
|
||||
H: Handler<Context>,
|
||||
SubH: Handler<Context>,
|
||||
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams,
|
||||
> {
|
||||
_phantom: PhantomData<(Context, H)>,
|
||||
handler: SubH,
|
||||
inherit: F,
|
||||
}
|
||||
impl<
|
||||
Context: crate::Context,
|
||||
H: Handler<Context>,
|
||||
SubH: Handler<Context>,
|
||||
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams,
|
||||
> Handler<Context> for InheritanceHandler<Context, H, SubH, F>
|
||||
{
|
||||
type Params = SubH::Params;
|
||||
type InheritedParams = Flat<H::Params, H::InheritedParams>;
|
||||
type Ok = SubH::Ok;
|
||||
type Err = SubH::Err;
|
||||
fn handle_sync(
|
||||
&self,
|
||||
HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params,
|
||||
}: HandleArgs<Context, Self>,
|
||||
) -> Result<Self::Ok, Self::Err> {
|
||||
self.handler.handle_sync(HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params: (self.inherit)(inherited_params.0, inherited_params.1),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, H, SubH, F> PrintCliResult<Context> for InheritanceHandler<Context, H, SubH, F>
|
||||
where
|
||||
Context: crate::Context,
|
||||
H: Handler<Context>,
|
||||
SubH: Handler<Context> + PrintCliResult<Context>,
|
||||
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams,
|
||||
{
|
||||
fn print(
|
||||
&self,
|
||||
HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params,
|
||||
}: HandleArgs<Context, Self>,
|
||||
result: Self::Ok,
|
||||
) -> Result<(), Self::Err> {
|
||||
self.handler.print(
|
||||
HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params: (self.inherit)(inherited_params.0, inherited_params.1),
|
||||
},
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context: crate::Context, H: Handler<Context>> ParentHandler<Context, H> {
|
||||
pub fn subcommand<SubH>(mut self, method: &'static str, handler: SubH) -> Self
|
||||
where
|
||||
SubH: Handler<Context, InheritedParams = NoParams> + PrintCliResult<Context> + 'static,
|
||||
SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned,
|
||||
SubH::Ok: Serialize + DeserializeOwned,
|
||||
RpcError: From<SubH::Err>,
|
||||
{
|
||||
self.subcommands.insert(
|
||||
method,
|
||||
DynHandler::WithCli(Box::new(AnyHandler {
|
||||
_ctx: PhantomData,
|
||||
handler: WithCliBindings {
|
||||
_ctx: PhantomData,
|
||||
handler,
|
||||
},
|
||||
})),
|
||||
);
|
||||
self
|
||||
}
|
||||
pub fn subcommand_with_inherited<SubH, F>(
|
||||
mut self,
|
||||
method: &'static str,
|
||||
handler: SubH,
|
||||
inherit: F,
|
||||
) -> Self
|
||||
where
|
||||
SubH: Handler<Context> + PrintCliResult<Context> + 'static,
|
||||
SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned,
|
||||
SubH::Ok: Serialize + DeserializeOwned,
|
||||
H: 'static,
|
||||
H::Params: DeserializeOwned,
|
||||
H::InheritedParams: DeserializeOwned,
|
||||
RpcError: From<SubH::Err>,
|
||||
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static,
|
||||
{
|
||||
self.subcommands.insert(
|
||||
method,
|
||||
DynHandler::WithCli(Box::new(AnyHandler {
|
||||
_ctx: PhantomData,
|
||||
handler: WithCliBindings {
|
||||
_ctx: PhantomData,
|
||||
handler: InheritanceHandler::<Context, H, SubH, F> {
|
||||
_phantom: PhantomData,
|
||||
handler,
|
||||
inherit,
|
||||
},
|
||||
},
|
||||
})),
|
||||
);
|
||||
self
|
||||
}
|
||||
pub fn subcommand_no_cli<SubH>(mut self, method: &'static str, handler: SubH) -> Self
|
||||
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,
|
||||
method: &'static str,
|
||||
handler: SubH,
|
||||
inherit: F,
|
||||
) -> Self
|
||||
where
|
||||
SubH: Handler<Context> + 'static,
|
||||
SubH::Params: DeserializeOwned,
|
||||
SubH::Ok: Serialize,
|
||||
H: 'static,
|
||||
H::Params: DeserializeOwned,
|
||||
H::InheritedParams: DeserializeOwned,
|
||||
RpcError: From<SubH::Err>,
|
||||
F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static,
|
||||
{
|
||||
self.subcommands.insert(
|
||||
method,
|
||||
DynHandler::WithoutCli(Box::new(AnyHandler {
|
||||
_ctx: PhantomData,
|
||||
handler: InheritanceHandler::<Context, H, SubH, F> {
|
||||
_phantom: PhantomData,
|
||||
handler,
|
||||
inherit,
|
||||
},
|
||||
})),
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, H> Handler<Context> for ParentHandler<Context, H>
|
||||
where
|
||||
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 InheritedParams = H::InheritedParams;
|
||||
type Ok = Value;
|
||||
type Err = RpcError;
|
||||
fn handle_sync(
|
||||
&self,
|
||||
HandleArgs {
|
||||
context,
|
||||
mut parent_method,
|
||||
mut method,
|
||||
params,
|
||||
inherited_params,
|
||||
}: HandleArgs<Context, Self>,
|
||||
) -> Result<Self::Ok, Self::Err> {
|
||||
if let Some(cmd) = method.pop_front() {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, H> CliBindings<Context> for ParentHandler<Context, H>
|
||||
where
|
||||
Context: crate::Context,
|
||||
H: CliBindings<Context>,
|
||||
H::Params: FromArgMatches + CommandFactory + Serialize,
|
||||
H::InheritedParams: Serialize,
|
||||
H::Ok: PrintCliResult<Context> + Serialize + DeserializeOwned,
|
||||
RpcError: From<H::Err>,
|
||||
{
|
||||
fn cli_command(&self) -> Command {
|
||||
H::Params::command().subcommands(self.subcommands.iter().filter_map(|(method, handler)| {
|
||||
match handler {
|
||||
DynHandler::WithCli(h) => Some(h.cli_command().name(method)),
|
||||
DynHandler::WithoutCli(_) => None,
|
||||
}
|
||||
}))
|
||||
}
|
||||
fn cli_parse(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
) -> Result<(VecDeque<&'static str>, Value), clap::Error> {
|
||||
let (_, root_params) = self.handler.cli_parse(matches)?;
|
||||
if let Some((sub, matches)) = matches.subcommand() {
|
||||
if let Some((sub, DynHandler::WithCli(h))) = self.subcommands.get_key_value(sub) {
|
||||
let (mut method, params) = h.cli_parse(matches)?;
|
||||
method.push_front(*sub);
|
||||
return Ok((
|
||||
method,
|
||||
combine(root_params, params).map_err(|e| {
|
||||
clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e)
|
||||
})?,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok((VecDeque::new(), root_params))
|
||||
}
|
||||
fn cli_display(
|
||||
&self,
|
||||
HandleArgs {
|
||||
context,
|
||||
mut parent_method,
|
||||
mut method,
|
||||
params,
|
||||
inherited_params,
|
||||
}: HandleArgs<Context, Self>,
|
||||
result: Self::Ok,
|
||||
) -> Result<(), Self::Err> {
|
||||
if let Some(cmd) = method.pop_front() {
|
||||
parent_method.push(cmd);
|
||||
if let Some(DynHandler::WithCli(sub_handler)) = self.subcommands.get(cmd) {
|
||||
sub_handler.cli_display(
|
||||
HandleAnyArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params: imbl_value::to_value(&Flat(params, inherited_params))
|
||||
.map_err(invalid_params)?,
|
||||
},
|
||||
result,
|
||||
)
|
||||
} else {
|
||||
Err(yajrc::METHOD_NOT_FOUND_ERROR)
|
||||
}
|
||||
} else {
|
||||
self.handler
|
||||
.cli_display(
|
||||
HandleArgs {
|
||||
context,
|
||||
parent_method,
|
||||
method,
|
||||
params,
|
||||
inherited_params,
|
||||
},
|
||||
imbl_value::from_value(result).map_err(internal_error)?,
|
||||
)
|
||||
.map_err(RpcError::from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FromFn<F, T, E, Args> {
|
||||
_phantom: PhantomData<(T, E, Args)>,
|
||||
function: F,
|
||||
}
|
||||
|
||||
pub fn from_fn<F, T, E, Args>(function: F) -> FromFn<F, T, E, Args> {
|
||||
FromFn {
|
||||
function,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, F, T, E> Handler<Context> for FromFn<F, T, E, ()>
|
||||
where
|
||||
Context: crate::Context,
|
||||
F: Fn() -> Result<T, E>,
|
||||
{
|
||||
type Params = NoParams;
|
||||
type InheritedParams = NoParams;
|
||||
type Ok = T;
|
||||
type Err = E;
|
||||
fn handle_sync(&self, _: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err> {
|
||||
(self.function)()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, F, T, E> Handler<Context> for FromFn<F, T, E, (Context,)>
|
||||
where
|
||||
Context: crate::Context,
|
||||
F: Fn(Context) -> Result<T, E>,
|
||||
{
|
||||
type Params = NoParams;
|
||||
type InheritedParams = NoParams;
|
||||
type Ok = T;
|
||||
type Err = E;
|
||||
fn handle_sync(&self, handle_args: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err> {
|
||||
(self.function)(handle_args.context)
|
||||
}
|
||||
}
|
||||
impl<Context, F, T, E, Params> Handler<Context> for FromFn<F, T, E, (Context, Params)>
|
||||
where
|
||||
Context: crate::Context,
|
||||
F: Fn(Context, Params) -> Result<T, E>,
|
||||
Params: DeserializeOwned,
|
||||
{
|
||||
type Params = Params;
|
||||
type InheritedParams = NoParams;
|
||||
type Ok = T;
|
||||
type Err = E;
|
||||
fn handle_sync(&self, handle_args: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err> {
|
||||
let HandleArgs {
|
||||
context, params, ..
|
||||
} = handle_args;
|
||||
(self.function)(context, params)
|
||||
}
|
||||
}
|
||||
impl<Context, F, T, E, Params, InheritedParams> Handler<Context>
|
||||
for FromFn<F, T, E, (Context, Params, InheritedParams)>
|
||||
where
|
||||
Context: crate::Context,
|
||||
F: Fn(Context, Params, InheritedParams) -> Result<T, E>,
|
||||
Params: DeserializeOwned,
|
||||
InheritedParams: DeserializeOwned,
|
||||
{
|
||||
type Params = Params;
|
||||
type InheritedParams = InheritedParams;
|
||||
type Ok = T;
|
||||
type Err = E;
|
||||
fn handle_sync(&self, handle_args: HandleArgs<Context, Self>) -> Result<Self::Ok, Self::Err> {
|
||||
let HandleArgs {
|
||||
context,
|
||||
params,
|
||||
inherited_params,
|
||||
..
|
||||
} = handle_args;
|
||||
(self.function)(context, params, inherited_params)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "this is db stuff")]
|
||||
struct DbParams {}
|
||||
|
||||
// Server::new(
|
||||
// ParentCommand::new()
|
||||
// .subcommand("foo", from_fn(foo))
|
||||
// .subcommand("db",
|
||||
// ParentCommand::new::<DbParams>()
|
||||
// .subcommand("dump", from_fn(dump))
|
||||
// )
|
||||
// )
|
||||
|
||||
// Server::new<Error = Error>()
|
||||
// .handle(
|
||||
// "db",
|
||||
// with_description("Description maybe?")
|
||||
// .handle("dump", from_fn(dump_route))
|
||||
// )
|
||||
// .handle(
|
||||
// "server",
|
||||
// no_description()
|
||||
// .handle("version", from_fn(version))
|
||||
// )
|
||||
|
||||
// #[derive(clap::Parser)]
|
||||
// struct DumpParams {
|
||||
// test: Option<String>
|
||||
// }
|
||||
|
||||
// fn dump_route(context: Context, param: Param<MyRouteParams>) -> Result<Value, Error> {
|
||||
// Ok(json!({
|
||||
// "db": {}
|
||||
// }))
|
||||
// }
|
||||
|
||||
// fn version() -> &'static str {
|
||||
// "1.0.0"
|
||||
// }
|
||||
@@ -1,6 +1,7 @@
|
||||
pub use cli::*;
|
||||
pub use command::*;
|
||||
// pub use command::*;
|
||||
pub use context::Context;
|
||||
pub use handler::*;
|
||||
/// `#[command(...)]`
|
||||
/// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only)
|
||||
/// - `#[command(rpc_only)]` -> no CLI bindings (leaf commands only)
|
||||
@@ -28,6 +29,7 @@ pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc};
|
||||
|
||||
mod cli;
|
||||
mod command;
|
||||
mod handler;
|
||||
// pub mod command_helpers;
|
||||
mod context;
|
||||
// mod metadata;
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
use futures::FutureExt;
|
||||
use hyper::body::Buf;
|
||||
use hyper::header::HeaderValue;
|
||||
use hyper::http::request::Parts as RequestParts;
|
||||
use hyper::http::response::Parts as ResponseParts;
|
||||
use hyper::http::Error as HttpError;
|
||||
use hyper::server::conn::AddrIncoming;
|
||||
use hyper::server::{Builder, Server};
|
||||
use hyper::{Body, HeaderMap, Request, Response, StatusCode};
|
||||
use lazy_static::lazy_static;
|
||||
use serde_json::{Map, Value};
|
||||
use url::Host;
|
||||
use yajrc::{AnyRpcMethod, GenericRpcMethod, Id, RpcError, RpcRequest, RpcResponse};
|
||||
|
||||
use crate::{Context, Metadata};
|
||||
|
||||
lazy_static! {
|
||||
#[cfg(feature = "cbor")]
|
||||
static ref CBOR_INTERNAL_ERROR: Vec<u8> =
|
||||
serde_cbor::to_vec(&RpcResponse::<AnyRpcMethod<'static>>::from(yajrc::INTERNAL_ERROR)).unwrap();
|
||||
static ref JSON_INTERNAL_ERROR: Vec<u8> =
|
||||
serde_json::to_vec(&RpcResponse::<AnyRpcMethod<'static>>::from(yajrc::INTERNAL_ERROR)).unwrap();
|
||||
}
|
||||
|
||||
pub fn make_builder<Ctx: Context>(ctx: &Ctx) -> Builder<AddrIncoming> {
|
||||
let addr = match ctx.host() {
|
||||
Host::Ipv4(ip) => (ip, ctx.port()).into(),
|
||||
Host::Ipv6(ip) => (ip, ctx.port()).into(),
|
||||
Host::Domain(localhost) if localhost == "localhost" => ([127, 0, 0, 1], ctx.port()).into(),
|
||||
_ => ([0, 0, 0, 0], ctx.port()).into(),
|
||||
};
|
||||
Server::bind(&addr)
|
||||
}
|
||||
|
||||
pub async fn make_request(
|
||||
req_parts: &RequestParts,
|
||||
req_body: Body,
|
||||
) -> Result<RpcRequest<GenericRpcMethod<String, Map<String, Value>>>, RpcError> {
|
||||
let body = hyper::body::aggregate(req_body).await?.reader();
|
||||
let rpc_req: RpcRequest<GenericRpcMethod<String, Map<String, Value>>>;
|
||||
#[cfg(feature = "cbor")]
|
||||
if req_parts
|
||||
.headers
|
||||
.get("content-type")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
== Some("application/cbor")
|
||||
{
|
||||
rpc_req = serde_cbor::from_reader(body)?;
|
||||
} else {
|
||||
rpc_req = serde_json::from_reader(body)?;
|
||||
}
|
||||
#[cfg(not(feature = "cbor"))]
|
||||
{
|
||||
rpc_req = serde_json::from_reader(body)?;
|
||||
}
|
||||
|
||||
Ok(rpc_req)
|
||||
}
|
||||
|
||||
pub fn to_response<F: Fn(i32) -> StatusCode>(
|
||||
req_headers: &HeaderMap<HeaderValue>,
|
||||
mut res_parts: ResponseParts,
|
||||
res: Result<(Option<Id>, Result<Value, RpcError>), RpcError>,
|
||||
status_code_fn: F,
|
||||
) -> Result<Response<Body>, HttpError> {
|
||||
let rpc_res: RpcResponse = match res {
|
||||
Ok((id, result)) => RpcResponse { id, result },
|
||||
Err(e) => e.into(),
|
||||
};
|
||||
let body;
|
||||
#[cfg(feature = "cbor")]
|
||||
if req_headers
|
||||
.get("accept")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.iter()
|
||||
.flat_map(|s| s.split(","))
|
||||
.map(|s| s.trim())
|
||||
.any(|s| s == "application/cbor")
|
||||
// prefer cbor if accepted
|
||||
{
|
||||
res_parts
|
||||
.headers
|
||||
.insert("content-type", HeaderValue::from_static("application/cbor"));
|
||||
body = serde_cbor::to_vec(&rpc_res).unwrap_or_else(|_| CBOR_INTERNAL_ERROR.clone());
|
||||
} else {
|
||||
res_parts
|
||||
.headers
|
||||
.insert("content-type", HeaderValue::from_static("application/json"));
|
||||
body = serde_json::to_vec(&rpc_res).unwrap_or_else(|_| JSON_INTERNAL_ERROR.clone());
|
||||
}
|
||||
#[cfg(not(feature = "cbor"))]
|
||||
{
|
||||
res_parts
|
||||
.headers
|
||||
.insert("content-type", HeaderValue::from_static("application/json"));
|
||||
body = serde_json::to_vec(&rpc_res).unwrap_or_else(|_| JSON_INTERNAL_ERROR.clone());
|
||||
}
|
||||
res_parts.headers.insert(
|
||||
"content-length",
|
||||
HeaderValue::from_str(&format!("{}", body.len()))?,
|
||||
);
|
||||
res_parts.status = match &rpc_res.result {
|
||||
Ok(_) => StatusCode::OK,
|
||||
Err(e) => status_code_fn(e.code),
|
||||
};
|
||||
Ok(Response::from_parts(res_parts, body.into()))
|
||||
}
|
||||
|
||||
pub type RpcHandler = Arc<
|
||||
dyn Fn(Request<Body>) -> BoxFuture<'static, Result<Response<Body>, HttpError>> + Send + Sync,
|
||||
>;
|
||||
|
||||
// &mut Request<Body> -> Result<Result<Future<&mut RpcRequest<...> -> Future<Result<Result<&mut Response<Body> -> Future<Result<(), HttpError>>, Response<Body>>, HttpError>>>, Response<Body>>, HttpError>
|
||||
pub type DynMiddleware<Metadata> = Box<
|
||||
dyn for<'a> Fn(
|
||||
&'a mut Request<Body>,
|
||||
Metadata,
|
||||
)
|
||||
-> BoxFuture<'a, Result<Result<DynMiddlewareStage2, Response<Body>>, HttpError>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
pub fn noop<M: Metadata>() -> DynMiddleware<M> {
|
||||
Box::new(|_, _| async { Ok(Ok(noop2())) }.boxed())
|
||||
}
|
||||
pub type DynMiddlewareStage2 = Box<
|
||||
dyn for<'a> FnOnce(
|
||||
&'a mut RequestParts,
|
||||
&'a mut RpcRequest<GenericRpcMethod<String, Map<String, Value>>>,
|
||||
) -> BoxFuture<
|
||||
'a,
|
||||
Result<Result<DynMiddlewareStage3, Response<Body>>, HttpError>,
|
||||
> + Send
|
||||
+ Sync,
|
||||
>;
|
||||
pub fn noop2() -> DynMiddlewareStage2 {
|
||||
Box::new(|_, _| async { Ok(Ok(noop3())) }.boxed())
|
||||
}
|
||||
pub type DynMiddlewareStage3 = Box<
|
||||
dyn for<'a> FnOnce(
|
||||
&'a mut ResponseParts,
|
||||
&'a mut Result<Value, RpcError>,
|
||||
) -> BoxFuture<
|
||||
'a,
|
||||
Result<Result<DynMiddlewareStage4, Response<Body>>, HttpError>,
|
||||
> + Send
|
||||
+ Sync,
|
||||
>;
|
||||
pub fn noop3() -> DynMiddlewareStage3 {
|
||||
Box::new(|_, _| async { Ok(Ok(noop4())) }.boxed())
|
||||
}
|
||||
pub type DynMiddlewareStage4 = Box<
|
||||
dyn for<'a> FnOnce(&'a mut Response<Body>) -> BoxFuture<'a, Result<(), HttpError>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
pub fn noop4() -> DynMiddlewareStage4 {
|
||||
Box::new(|_| async { Ok(()) }.boxed())
|
||||
}
|
||||
|
||||
pub fn constrain_middleware<
|
||||
'a,
|
||||
'b,
|
||||
'c,
|
||||
'd,
|
||||
M: Metadata,
|
||||
ReqFn: Fn(&'a mut Request<Body>, M) -> ReqFut + Clone,
|
||||
ReqFut: Future<Output = Result<Result<RpcReqFn, Response<Body>>, HttpError>> + 'a,
|
||||
RpcReqFn: FnOnce(
|
||||
&'b mut RequestParts,
|
||||
&'b mut RpcRequest<GenericRpcMethod<String, Map<String, Value>>>,
|
||||
) -> RpcReqFut,
|
||||
RpcReqFut: Future<Output = Result<Result<RpcResFn, Response<Body>>, HttpError>> + 'b,
|
||||
RpcResFn: FnOnce(&'c mut ResponseParts, &'c mut Result<Value, RpcError>) -> RpcResFut,
|
||||
RpcResFut: Future<Output = Result<Result<ResFn, Response<Body>>, HttpError>> + 'c,
|
||||
ResFn: FnOnce(&'d mut Response<Body>) -> ResFut,
|
||||
ResFut: Future<Output = Result<(), HttpError>> + 'd,
|
||||
>(
|
||||
f: ReqFn,
|
||||
) -> ReqFn {
|
||||
f
|
||||
}
|
||||
@@ -4,7 +4,8 @@ use futures::future::BoxFuture;
|
||||
use futures::{Future, FutureExt, Stream, StreamExt};
|
||||
use imbl_value::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Deserialize;
|
||||
use serde::ser::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yajrc::RpcError;
|
||||
|
||||
pub fn extract<T: DeserializeOwned>(value: &Value) -> Result<T, RpcError> {
|
||||
@@ -14,18 +15,20 @@ pub fn extract<T: DeserializeOwned>(value: &Value) -> Result<T, RpcError> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn combine(v1: Value, v2: Value) -> Result<Value, RpcError> {
|
||||
pub fn combine(v1: Value, v2: Value) -> Result<Value, imbl_value::Error> {
|
||||
let (Value::Object(mut v1), Value::Object(v2)) = (v1, v2) else {
|
||||
return Err(RpcError {
|
||||
data: Some("params must be object".into()),
|
||||
..yajrc::INVALID_PARAMS_ERROR
|
||||
return Err(imbl_value::Error {
|
||||
kind: imbl_value::ErrorKind::Serialization,
|
||||
source: serde_json::Error::custom("params must be object"),
|
||||
});
|
||||
};
|
||||
for (key, value) in v2 {
|
||||
if v1.insert(key.clone(), value).is_some() {
|
||||
return Err(RpcError {
|
||||
data: Some(format!("duplicate key: {key}").into()),
|
||||
..yajrc::INVALID_PARAMS_ERROR
|
||||
return Err(imbl_value::Error {
|
||||
kind: imbl_value::ErrorKind::Serialization,
|
||||
source: serde_json::Error::custom(lazy_format::lazy_format!(
|
||||
"duplicate key: {key}"
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -76,6 +79,29 @@ where
|
||||
Ok(Flat(a, b))
|
||||
}
|
||||
}
|
||||
impl<A, B> Serialize for Flat<A, B>
|
||||
where
|
||||
A: Serialize,
|
||||
B: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
#[derive(serde::Serialize)]
|
||||
struct FlatStruct<'a, A, B> {
|
||||
#[serde(flatten)]
|
||||
a: &'a A,
|
||||
#[serde(flatten)]
|
||||
b: &'a B,
|
||||
}
|
||||
FlatStruct {
|
||||
a: &self.0,
|
||||
b: &self.1,
|
||||
}
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_select_all<'a, T>(
|
||||
futs: &mut Vec<BoxFuture<'a, T>>,
|
||||
@@ -145,3 +171,17 @@ impl<'a, T> Stream for JobRunner<'a, T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Debug)]
|
||||
// pub enum Infallible {}
|
||||
// impl<T> From<Infallible> for T {
|
||||
// fn from(value: Infallible) -> Self {
|
||||
// match value {}
|
||||
// }
|
||||
// }
|
||||
// impl std::fmt::Display for Infallible {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// match *self {}
|
||||
// }
|
||||
// }
|
||||
// impl std::error::Error for Infallible {}
|
||||
|
||||
Reference in New Issue
Block a user