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