From 205a9fc2aa05ece114c4e36b762fb7ff67d3d77c Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 23 Jun 2019 12:52:04 +0200 Subject: [PATCH] api: split ApiHandler out of ApiMethodInfo The method info part is not generic after all. (Makes it easier to test different representations of ApiHandler without having to adapt all the other methods as well.) Signed-off-by: Wolfgang Bumiller --- proxmox-api-macro/src/api_macro.rs | 20 +++++++++++++------- proxmox-api/src/api_type.rs | 28 +++++++++++++++++++--------- proxmox-api/src/cli.rs | 4 ++-- proxmox-api/src/router.rs | 26 +++++++++++++------------- 4 files changed, 47 insertions(+), 31 deletions(-) diff --git a/proxmox-api-macro/src/api_macro.rs b/proxmox-api-macro/src/api_macro.rs index 1bdd72f5..0f7e23fb 100644 --- a/proxmox-api-macro/src/api_macro.rs +++ b/proxmox-api-macro/src/api_macro.rs @@ -194,20 +194,20 @@ fn handle_function( use std::iter::FromIterator; let arg_extraction = TokenStream::from_iter(arg_extraction.into_iter()); - // The router expects an ApiMethod, or more accurately, an object implementing ApiMethodInfo. + // The router expects an ApiMethod, or more accurately, an object implementing ApiHandler. // This is because we need access to a bunch of additional attributes of the functions both at // runtime and when doing command line parsing/completion/help output. // // When manually implementing methods, we usually just write them out as an `ApiMethod` which - // is a type requiring all the info made available by the ApiMethodInfo trait as members. + // is a type requiring all the info made available by the ApiHandler trait as members. // // While we could just generate a `const ApiMethod` for our functions, we would like them to // also be usable as functions simply because the syntax we use to create them makes them // *look* like functions, so it would be nice if they also *behaved* like real functions. // - // Therefore all the fields of an ApiMethod are accessed via methods from the ApiMethodInfo - // trait and we perform the same trick lazy_static does: Create a new type implementing - // ApiMethodInfo, and make its instance Deref to an actual function. + // Therefore all the fields of an ApiMethod are accessed via methods from the ApiHandler trait + // and we perform the same trick lazy_static does: Create a new type implementing ApiHandler, + // and make its instance Deref to an actual function. // This way the function can still be used normally. Validators for parameters will be // executed, serialization happens only when coming from the method's `handler`. @@ -334,8 +334,6 @@ fn handle_function( // Note that technically we don't need the `description` member in this trait, as this is // mostly used at compile time for documentation! impl ::proxmox::api::ApiMethodInfo for #struct_name { - type Body = #body_type; - fn description(&self) -> &'static str { #fn_api_description } @@ -356,10 +354,18 @@ fn handle_function( fn reload_timezone(&self) -> bool { #fn_api_reload_timezone } + } + + impl ::proxmox::api::ApiHandler for #struct_name { + type Body = #body_type; fn call(&self, params: ::serde_json::Value) -> ::proxmox::api::ApiFuture<#body_type> { #struct_name::wrapped_api_handler(params) } + + fn method_info(&self) -> &(dyn ::proxmox::api::ApiMethodInfo + Send + Sync) { + self as _ + } } }); diff --git a/proxmox-api/src/api_type.rs b/proxmox-api/src/api_type.rs index e4a61f56..4d95b692 100644 --- a/proxmox-api/src/api_type.rs +++ b/proxmox-api/src/api_type.rs @@ -8,17 +8,21 @@ use serde_json::{json, Value}; /// This contains all the info required to call, document, or command-line-complete parameters for /// a method. pub trait ApiMethodInfo { - type Body; - fn description(&self) -> &'static str; fn parameters(&self) -> &'static [Parameter]; fn return_type(&self) -> &'static TypeInfo; fn protected(&self) -> bool; fn reload_timezone(&self) -> bool; - fn call(&self, params: Value) -> super::ApiFuture; } -impl dyn ApiMethodInfo { +pub trait ApiHandler: ApiMethodInfo + Send + Sync { + type Body; + + fn call(&self, params: Value) -> super::ApiFuture; + fn method_info(&self) -> &(dyn ApiMethodInfo + Send + Sync); +} + +impl dyn ApiHandler { pub fn call_as(&self, params: Value) -> super::ApiFuture where Body: Into, @@ -97,8 +101,6 @@ pub struct ApiMethod { } impl ApiMethodInfo for ApiMethod { - type Body = Body; - fn description(&self) -> &'static str { self.description } @@ -118,13 +120,21 @@ impl ApiMethodInfo for ApiMethod { fn reload_timezone(&self) -> bool { self.reload_timezone } +} + +impl ApiHandler for ApiMethod { + type Body = Body; fn call(&self, params: Value) -> super::ApiFuture { (self.handler)(params) } + + fn method_info(&self) -> &(dyn ApiMethodInfo + Send + Sync) { + self as _ + } } -impl dyn ApiMethodInfo + Send + Sync { +impl dyn ApiMethodInfo + Send + Sync { pub fn api_dump(&self) -> Value { let parameters = Value::Object(std::iter::FromIterator::from_iter( self.parameters() @@ -311,7 +321,7 @@ pub trait UnifiedApiMethod: Send + Sync { impl UnifiedApiMethod for T where - T: ApiMethodInfo, + T: ApiHandler, T::Body: 'static + Into, { fn parameters(&self) -> &'static [Parameter] { @@ -319,6 +329,6 @@ where } fn call(&self, params: Value) -> super::ApiFuture { - (self as &dyn ApiMethodInfo).call_as(params) + (self as &dyn ApiHandler).call_as(params) } } diff --git a/proxmox-api/src/cli.rs b/proxmox-api/src/cli.rs index 416ba4e2..53f10d9a 100644 --- a/proxmox-api/src/cli.rs +++ b/proxmox-api/src/cli.rs @@ -8,7 +8,7 @@ use failure::{bail, format_err, Error}; use serde::Serialize; use serde_json::Value; -use super::{ApiMethodInfo, ApiOutput, Parameter, UnifiedApiMethod}; +use super::{ApiHandler, ApiOutput, Parameter, UnifiedApiMethod}; type MethodInfoRef = &'static dyn UnifiedApiMethod; @@ -87,7 +87,7 @@ impl Command { positional_args: &'static [&'static str], ) -> Self where - T: ApiMethodInfo, + T: ApiHandler, T::Body: 'static + Into, { Command::Method(Method::new(method, positional_args)) diff --git a/proxmox-api/src/router.rs b/proxmox-api/src/router.rs index 33c3d1cf..606ea996 100644 --- a/proxmox-api/src/router.rs +++ b/proxmox-api/src/router.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use serde_json::{json, Value}; -use super::ApiMethodInfo; +use super::ApiHandler; /// This enum specifies what to do when a subdirectory is requested from the current router. /// @@ -32,16 +32,16 @@ pub enum SubRoute { #[derive(Default)] pub struct Router { /// The `GET` http method. - pub get: Option<&'static (dyn ApiMethodInfo + Send + Sync)>, + pub get: Option<&'static (dyn ApiHandler + Send + Sync)>, /// The `PUT` http method. - pub put: Option<&'static (dyn ApiMethodInfo + Send + Sync)>, + pub put: Option<&'static (dyn ApiHandler + Send + Sync)>, /// The `POST` http method. - pub post: Option<&'static (dyn ApiMethodInfo + Send + Sync)>, + pub post: Option<&'static (dyn ApiHandler + Send + Sync)>, /// The `DELETE` http method. - pub delete: Option<&'static (dyn ApiMethodInfo + Send + Sync)>, + pub delete: Option<&'static (dyn ApiHandler + Send + Sync)>, /// Specifies the behavior of sub directories. See [`SubRoute`]. pub subroute: Option>, @@ -114,19 +114,19 @@ where let mut this = serde_json::Map::::new(); if let Some(get) = self.get { - this.insert("GET".to_string(), get.api_dump()); + this.insert("GET".to_string(), get.method_info().api_dump()); } if let Some(put) = self.put { - this.insert("PUT".to_string(), put.api_dump()); + this.insert("PUT".to_string(), put.method_info().api_dump()); } if let Some(post) = self.post { - this.insert("POST".to_string(), post.api_dump()); + this.insert("POST".to_string(), post.method_info().api_dump()); } if let Some(delete) = self.delete { - this.insert("DELETE".to_string(), delete.api_dump()); + this.insert("DELETE".to_string(), delete.method_info().api_dump()); } match &self.subroute { @@ -165,7 +165,7 @@ where /// Builder method to provide a `GET` method info. pub fn get(mut self, method: &'static I) -> Self where - I: ApiMethodInfo + Send + Sync, + I: ApiHandler + Send + Sync, { self.get = Some(method); self @@ -174,7 +174,7 @@ where /// Builder method to provide a `PUT` method info. pub fn put(mut self, method: &'static I) -> Self where - I: ApiMethodInfo + Send + Sync, + I: ApiHandler + Send + Sync, { self.put = Some(method); self @@ -183,7 +183,7 @@ where /// Builder method to provide a `POST` method info. pub fn post(mut self, method: &'static I) -> Self where - I: ApiMethodInfo + Send + Sync, + I: ApiHandler + Send + Sync, { self.post = Some(method); self @@ -192,7 +192,7 @@ where /// Builder method to provide a `DELETE` method info. pub fn delete(mut self, method: &'static I) -> Self where - I: ApiMethodInfo + Send + Sync, + I: ApiHandler + Send + Sync, { self.delete = Some(method); self