use std::collections::VecDeque; use std::ffi::OsString; use clap::{CommandFactory, FromArgMatches}; use futures::Future; use imbl_value::imbl::OrdMap; use imbl_value::Value; use reqwest::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE}; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; use serde::Serialize; use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; use url::Url; use yajrc::{Id, RpcError}; use crate::util::{internal_error, invalid_params, parse_error, without, Flat, PhantomData}; use crate::{ AnyHandler, CliBindings, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, Name, ParentHandler, PrintCliResult, }; type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; type RpcRequest<'a> = yajrc::RpcRequest>; type RpcResponse<'a> = yajrc::RpcResponse>; pub struct CliApp { _phantom: PhantomData<(Context, Config)>, make_ctx: Box Result + Send + Sync>, root_handler: ParentHandler, } impl CliApp { pub fn new Result + Send + Sync + 'static>( make_ctx: MakeCtx, root_handler: ParentHandler, ) -> Self { Self { _phantom: PhantomData::new(), make_ctx: Box::new(make_ctx), root_handler, } } pub fn run(self, args: impl IntoIterator) -> Result<(), RpcError> { let mut cmd = Config::command(); for (name, handler) in &self.root_handler.subcommands.0 { if let (Name(Some(name)), Some(cli)) = (name, handler.cli()) { cmd = cmd.subcommand(cli.cli_command().name(name)); } } let matches = cmd.get_matches_from(args); let config = Config::from_arg_matches(&matches)?; let ctx = (self.make_ctx)(config)?; let root_handler = AnyHandler::new(self.root_handler); let (method, params) = root_handler.cli_parse(&matches)?; let res = root_handler.handle_sync(HandleAnyArgs { context: ctx.clone(), parent_method: VecDeque::new(), method: method.clone(), params: params.clone(), inherited: crate::Empty {}, })?; root_handler.cli_display( HandleAnyArgs { context: ctx, parent_method: VecDeque::new(), method, params, inherited: crate::Empty {}, }, res, )?; Ok(()) } } pub trait CallRemote: crate::Context { fn call_remote( &self, method: &str, params: Value, extra: Extra, ) -> impl Future> + Send; } pub async fn call_remote_http( client: &Client, url: Url, method: &str, params: Value, ) -> Result { let rpc_req = RpcRequest { id: Some(Id::Number(0.into())), method: GenericRpcMethod::new(method), params, }; let mut req = client.request(Method::POST, 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(&rpc_req)?; } let res = req .header(CONTENT_LENGTH, body.len()) .body(body) .send() .await?; match res .headers() .get(CONTENT_TYPE) .and_then(|v| v.to_str().ok()) { Some("application/json") => { serde_json::from_slice::(&*res.bytes().await.map_err(internal_error)?) .map_err(parse_error)? .result } #[cfg(feature = "cbor")] Some("application/cbor") => { serde_cbor::from_slice::(&*res.bytes().await.map_err(internal_error)?) .map_err(parse_error)? .result } _ => Err(internal_error("missing content type")), } } pub async fn call_remote_socket( connection: impl AsyncRead + AsyncWrite, method: &str, params: Value, ) -> Result { let rpc_req = RpcRequest { id: Some(Id::Number(0.into())), method: GenericRpcMethod::new(method), params, }; let conn = connection; tokio::pin!(conn); let mut buf = serde_json::to_vec(&rpc_req).map_err(|e| RpcError { data: Some(e.to_string().into()), ..yajrc::INTERNAL_ERROR })?; buf.push(b'\n'); conn.write_all(&buf).await.map_err(|e| RpcError { data: Some(e.to_string().into()), ..yajrc::INTERNAL_ERROR })?; let mut line = String::new(); BufReader::new(conn).read_line(&mut line).await?; serde_json::from_str::(&line) .map_err(parse_error)? .result } pub struct CallRemoteHandler { _phantom: PhantomData<(Context, RemoteContext, Extra)>, handler: RemoteHandler, } impl CallRemoteHandler { pub fn new(handler: RemoteHandler) -> Self { Self { _phantom: PhantomData::new(), handler: handler, } } } impl Clone for CallRemoteHandler { fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), handler: self.handler.clone(), } } } impl std::fmt::Debug for CallRemoteHandler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("CallRemoteHandler").finish() } } impl HandlerTypes for CallRemoteHandler where RemoteHandler: HandlerTypes, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Send + Sync + 'static, { type Params = Flat; type InheritedParams = RemoteHandler::InheritedParams; type Ok = RemoteHandler::Ok; type Err = RemoteHandler::Err; } impl HandlerFor for CallRemoteHandler where Context: CallRemote, RemoteContext: crate::Context, RemoteHandler: HandlerFor, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Serialize + Send + Sync + 'static, { async fn handle_async( &self, handle_args: HandlerArgsFor, ) -> Result { let full_method = handle_args .parent_method .into_iter() .chain(handle_args.method) .collect::>(); match handle_args .context .call_remote( &full_method.join("."), without(handle_args.raw_params.clone(), &handle_args.params.1) .map_err(invalid_params)?, handle_args.params.1, ) .await { Ok(a) => imbl_value::from_value(a) .map_err(internal_error) .map_err(Self::Err::from), Err(e) => Err(Self::Err::from(e)), } } fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.handler.metadata(method) } fn method_from_dots(&self, method: &str) -> Option> { self.handler.method_from_dots(method) } } impl PrintCliResult for CallRemoteHandler where Context: CallRemote, RemoteHandler: PrintCliResult, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Send + Sync + 'static, { fn print( &self, HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.print( HandlerArgs { context, parent_method, method, params: params.0, inherited_params, raw_params, }, result, ) } } impl CliBindings for CallRemoteHandler where Context: crate::Context, RemoteHandler: CliBindings, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Send + Sync + 'static, { fn cli_command(&self) -> clap::Command { self.handler.cli_command() } fn cli_parse( &self, matches: &clap::ArgMatches, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { self.handler.cli_parse(matches) } fn cli_display( &self, HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.cli_display( HandlerArgs { context, parent_method, method, params: params.0, inherited_params, raw_params, }, result, ) } }