diff --git a/src/api2.rs b/src/api2.rs index b92d9ab3..93816602 100644 --- a/src/api2.rs +++ b/src/api2.rs @@ -35,7 +35,7 @@ pub fn router() -> Router { let route = Router::new() .get(ApiMethod::new( - |_,_,_| Ok(json!([ + || Ok(json!([ {"subdir": "access"}, {"subdir": "admin"}, {"subdir": "config"}, diff --git a/src/api2/access.rs b/src/api2/access.rs index ee094624..f7e2b5e9 100644 --- a/src/api2/access.rs +++ b/src/api2/access.rs @@ -70,7 +70,7 @@ pub fn router() -> Router { let route = Router::new() .get(ApiMethod::new( - |_,_,_| Ok(json!([ + || Ok(json!([ {"subdir": "ticket"} ])), ObjectSchema::new("Directory index."))) diff --git a/src/api2/admin.rs b/src/api2/admin.rs index a6eca7a7..4efce8a9 100644 --- a/src/api2/admin.rs +++ b/src/api2/admin.rs @@ -8,7 +8,7 @@ pub fn router() -> Router { let route = Router::new() .get(ApiMethod::new( - |_,_,_| Ok(json!([ + || Ok(json!([ {"subdir": "datastore"} ])), ObjectSchema::new("Directory index."))) diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index b29ae0d7..8f2a4719 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -388,7 +388,7 @@ pub fn router() -> Router { let datastore_info = Router::new() .get(ApiMethod::new( - |_,_,_| Ok(json!([ + || Ok(json!([ {"subdir": "backups" }, {"subdir": "pxar" }, {"subdir": "gc" }, diff --git a/src/api2/config.rs b/src/api2/config.rs index b5d50e02..36c6103d 100644 --- a/src/api2/config.rs +++ b/src/api2/config.rs @@ -11,7 +11,7 @@ pub fn router() -> Router { let route = Router::new() .get(ApiMethod::new( - |_,_,_| Ok(json!([ + || Ok(json!([ {"subdir": "datastore"}, ])), ObjectSchema::new("Directory index."))) diff --git a/src/api2/node.rs b/src/api2/node.rs index 486add2f..5f1005dd 100644 --- a/src/api2/node.rs +++ b/src/api2/node.rs @@ -35,7 +35,7 @@ pub fn router() -> Router { let route = Router::new() .get(ApiMethod::new( - |_,_,_| Ok(json!([ + || Ok(json!([ {"subdir": "dns"}, {"subdir": "network"}, {"subdir": "services"}, diff --git a/src/api2/node/services.rs b/src/api2/node/services.rs index 5890d174..91f6b660 100644 --- a/src/api2/node/services.rs +++ b/src/api2/node/services.rs @@ -224,7 +224,7 @@ pub fn router() -> Router { let service_api = Router::new() .get(ApiMethod::new( - |_,_,_| { + || { let mut result = vec![]; for cmd in &["state", "start", "stop", "restart", "reload"] { result.push(json!({"subdir": cmd })); diff --git a/src/api2/node/tasks.rs b/src/api2/node/tasks.rs index a0b7c652..d8f62f98 100644 --- a/src/api2/node/tasks.rs +++ b/src/api2/node/tasks.rs @@ -175,7 +175,7 @@ pub fn router() -> Router { let upid_api = Router::new() .get(ApiMethod::new( - |_,_,_| { + || { let mut result = vec![]; for cmd in &["log", "status"] { result.push(json!({"subdir": cmd })); diff --git a/src/api_schema.rs b/src/api_schema.rs index ebbe7c12..27156df3 100644 --- a/src/api_schema.rs +++ b/src/api_schema.rs @@ -12,6 +12,7 @@ mod schema; pub use schema::*; +pub mod api_handler; pub mod registry; #[macro_use] pub mod router; diff --git a/src/api_schema/api_handler.rs b/src/api_schema/api_handler.rs new file mode 100644 index 00000000..ea06481f --- /dev/null +++ b/src/api_schema/api_handler.rs @@ -0,0 +1,135 @@ +use failure::Error; +use serde_json::Value; + +use super::router::{ApiMethod, RpcEnvironment}; + +pub type ApiHandlerFn = Box< + dyn Fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result + + Send + Sync + 'static +>; + +pub trait WrapApiHandler { + fn wrap(self) -> ApiHandlerFn; +} + +// fn() +impl WrapApiHandler<(), R, ()> for F +where + F: Fn() -> Result + Send + Sync + 'static, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |_value, _method, _rpc_env| { + Ok(serde_json::to_value((self)()?)?) + }) + } +} + +// fn(Arg) +impl WrapApiHandler<(A,), R, ()> for F +where + F: Fn(A) -> Result + Send + Sync + 'static, + A: serde::de::DeserializeOwned, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |value, _method, _rpc_env| { + Ok(serde_json::to_value((self)(serde_json::from_value(value)?)?)?) + }) + } +} + +// fn(&ApiMethod) +impl WrapApiHandler<(), R, (ApiMethod,)> for F +where + F: Fn(&ApiMethod) -> Result + Send + Sync + 'static, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |_value, method, _rpc_env| { + Ok(serde_json::to_value((self)(method)?)?) + }) + } +} + +// fn(Arg, &ApiMethod) +impl WrapApiHandler<(A,), R, (ApiMethod,)> for F +where + F: Fn(A, &ApiMethod) -> Result + Send + Sync + 'static, + A: serde::de::DeserializeOwned, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |value, method, _rpc_env| { + Ok(serde_json::to_value((self)( + serde_json::from_value(value)?, + method, + )?)?) + }) + } +} + +// RpcEnvironment is a trait, so use a "marker" type for it instead: +pub struct RpcEnvArg(); + +// fn(&mut dyn RpcEnvironment) +impl WrapApiHandler<(), R, (RpcEnvArg,)> for F +where + F: Fn(&mut dyn RpcEnvironment) -> Result + Send + Sync + 'static, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |_value, _method, rpc_env| { + Ok(serde_json::to_value((self)(rpc_env)?)?) + }) + } +} + +// fn(Arg, &mut dyn RpcEnvironment) +impl WrapApiHandler<(A,), R, (RpcEnvArg,)> for F +where + F: Fn(A, &mut dyn RpcEnvironment) -> Result + Send + Sync + 'static, + A: serde::de::DeserializeOwned, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |value, _method, rpc_env| { + Ok(serde_json::to_value((self)( + serde_json::from_value(value)?, + rpc_env, + )?)?) + }) + } +} + +// fn(&ApiMethod, &mut dyn RpcEnvironment) +impl WrapApiHandler<(), R, (ApiMethod, RpcEnvArg,)> for F +where + F: Fn(&ApiMethod, &mut dyn RpcEnvironment) -> Result + Send + Sync + 'static, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |_value, method, rpc_env| { + Ok(serde_json::to_value((self)(method, rpc_env)?)?) + }) + } +} + +// fn(Arg, &ApiMethod, &mut dyn RpcEnvironment) +impl WrapApiHandler<(A,), R, (ApiMethod, RpcEnvArg,)> for F +where + F: Fn(A, &ApiMethod, &mut dyn RpcEnvironment) -> Result + Send + Sync + 'static, + A: serde::de::DeserializeOwned, + R: serde::Serialize, +{ + fn wrap(self) -> ApiHandlerFn { + Box::new(move |value, method, rpc_env| { + Ok(serde_json::to_value((self)( + serde_json::from_value(value)?, + method, + rpc_env, + )?)?) + }) + } +} + diff --git a/src/api_schema/router.rs b/src/api_schema/router.rs index 02328a75..2c2e0dc6 100644 --- a/src/api_schema/router.rs +++ b/src/api_schema/router.rs @@ -10,6 +10,8 @@ use hyper::{Body, Response, StatusCode}; use hyper::rt::Future; use hyper::http::request::Parts; +use super::api_handler::*; + pub type BoxFut = Box, Error = failure::Error> + Send>; /// Abstract Interface for API methods to interact with the environment @@ -72,9 +74,10 @@ macro_rules! http_err { }} } -type ApiHandlerFn = fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result; - -type ApiAsyncHandlerFn = fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result; +type ApiAsyncHandlerFn = Box< + dyn Fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result + + Send + Sync + 'static +>; /// This struct defines synchronous API call which returns the restulkt as json `Value` pub struct ApiMethod { @@ -89,15 +92,28 @@ pub struct ApiMethod { /// Return type Schema pub returns: Arc, /// Handler function - pub handler: ApiHandlerFn, + pub handler: Option, } impl ApiMethod { - pub fn new(handler: ApiHandlerFn, parameters: ObjectSchema) -> Self { + pub fn new(func: F, parameters: ObjectSchema) -> Self + where + F: WrapApiHandler, + { Self { parameters, - handler, + handler: Some(func.wrap()), + returns: Arc::new(Schema::Null), + protected: false, + reload_timezone: false, + } + } + + pub fn new_dummy(parameters: ObjectSchema) -> Self { + Self { + parameters, + handler: None, returns: Arc::new(Schema::Null), protected: false, reload_timezone: false, @@ -134,10 +150,14 @@ pub struct ApiAsyncMethod { impl ApiAsyncMethod { - pub fn new(handler: ApiAsyncHandlerFn, parameters: ObjectSchema) -> Self { + pub fn new(handler: F, parameters: ObjectSchema) -> Self + where + F: Fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result + + Send + Sync + 'static, + { Self { parameters, - handler, + handler: Box::new(handler), returns: Arc::new(Schema::Null), } } diff --git a/src/cli/command.rs b/src/cli/command.rs index ee9503ee..25a53f34 100644 --- a/src/cli/command.rs +++ b/src/cli/command.rs @@ -1,7 +1,6 @@ use failure::*; use std::collections::HashMap; use std::collections::HashSet; -use serde_json::Value; use crate::api_schema::*; use crate::api_schema::router::*; @@ -268,7 +267,7 @@ fn handle_simple_command( } }; - if (cli_cmd.info.handler as *const fn()) == (dummy_help as *const fn()) { + if cli_cmd.info.handler.is_none() { let prefix = prefix.split(' ').next().unwrap().to_string(); print_help(top_def, prefix, &rest, params["verbose"].as_bool()); return; @@ -282,7 +281,7 @@ fn handle_simple_command( let mut rpcenv = CliEnvironment::new(); - match (cli_cmd.info.handler)(params, &cli_cmd.info, &mut rpcenv) { + match (cli_cmd.info.handler.as_ref().unwrap())(params, &cli_cmd.info, &mut rpcenv) { Ok(value) => { println!("Result: {}", serde_json::to_string_pretty(&value).unwrap()); } @@ -603,8 +602,7 @@ pub fn print_bash_completion(def: &CommandLineInterface) { fn help_command_def() -> CliCommand { CliCommand::new( - ApiMethod::new( - dummy_help, + ApiMethod::new_dummy( ObjectSchema::new("Get help about specified command.") .optional("verbose", BooleanSchema::new("Verbose help.")) ) @@ -693,10 +691,6 @@ pub struct CliCommandMap { pub commands: HashMap, } -fn dummy_help(_param: Value, _info: &ApiMethod, _rpcenv: &mut RpcEnvironment) -> Result { - panic!("internal error"); // this is just a place holder - never call this -} - impl CliCommandMap { pub fn new() -> Self { diff --git a/src/server/rest.rs b/src/server/rest.rs index 1cce06ed..eda55f36 100644 --- a/src/server/rest.rs +++ b/src/server/rest.rs @@ -239,7 +239,7 @@ fn handle_sync_api_request( let resp = params .and_then(move |params| { let mut delay = false; - let resp = match (info.handler)(params, info, &mut rpcenv) { + let resp = match (info.handler.as_ref().unwrap())(params, info, &mut rpcenv) { Ok(data) => (formatter.format_result)(data, &rpcenv), Err(err) => { if let Some(httperr) = err.downcast_ref::() {