diff --git a/proxmox-api/Cargo.toml b/proxmox-api/Cargo.toml index f2245576..8d05bdf0 100644 --- a/proxmox-api/Cargo.toml +++ b/proxmox-api/Cargo.toml @@ -9,7 +9,6 @@ bytes = "0.4" failure = "0.1" futures-preview = "0.3.0-alpha" http = "0.1" -hyper = { version = "0.13.0-alpha.1" } proxmox-tools = { version = "0.1", path = "../proxmox-tools" } regex = "1.0" rustyline = "5.0.4" @@ -19,5 +18,12 @@ serde_json = "1.0" textwrap = "0.11" url = "1.7" +hyper = { version = "0.13.0-alpha.1", optional = true } + [dev-dependencies] lazy_static = "1.3" + +[features] +default = [ "router", "cli" ] +router = [ "hyper" ] +cli = [ "router", "hyper" ] diff --git a/proxmox-api/src/format.rs b/proxmox-api/src/format.rs index 3abe9120..94d1f256 100644 --- a/proxmox-api/src/format.rs +++ b/proxmox-api/src/format.rs @@ -1,11 +1,11 @@ //! Module to generate and format API Documenation -use failure::*; + +use failure::Error; use std::io::Write; -use super::router::{Router, SubRoute}; -use super::schema::*; -use super::{ApiHandler, ApiMethod}; +use crate::schema::*; +use crate::{ApiHandler, ApiMethod}; /// Enumerate different styles to display parameters/properties. #[derive(Copy, Clone)] @@ -256,10 +256,12 @@ fn dump_method_definition(method: &str, path: &str, def: Option<&ApiMethod>) -> /// Generate ReST Documentaion for a complete API defined by a ``Router``. pub fn dump_api( output: &mut dyn Write, - router: &Router, + router: &crate::Router, path: &str, mut pos: usize, ) -> Result<(), Error> { + use crate::SubRoute; + let mut cond_print = |x| -> Result<_, Error> { if let Some(text) = x { if pos > 0 { diff --git a/proxmox-api/src/lib.rs b/proxmox-api/src/lib.rs index bccab97f..7326fb7a 100644 --- a/proxmox-api/src/lib.rs +++ b/proxmox-api/src/lib.rs @@ -1,189 +1,35 @@ //! Proxmox API module. This provides utilities for HTTP and command line APIs. -use std::fmt; -use std::future::Future; -use std::pin::Pin; - -use failure::Error; -use http::request::Parts; -use http::Response; -use hyper::Body; -use serde_json::Value; - #[doc(hidden)] pub mod const_regex; #[doc(hidden)] pub mod error; -pub mod format; -#[doc(hidden)] -pub mod router; -#[doc(hidden)] -pub mod rpc_environment; pub mod schema; -pub mod cli; - -use schema::{ObjectSchema, Schema}; - #[doc(inline)] pub use const_regex::ConstRegexPattern; -#[doc(inline)] -pub use rpc_environment::{RpcEnvironment, RpcEnvironmentType}; - -#[doc(inline)] -pub use router::{Router, SubRoute, SubdirMap}; - #[doc(inline)] pub use error::HttpError; -/// A synchronous API handler gets a json Value as input and returns a json Value as output. -/// -/// Most API handler are synchronous. Use this to define such handler: -/// ``` -/// # use failure::*; -/// # use serde_json::{json, Value}; -/// # use proxmox_api::{*, schema::*}; -/// # -/// fn hello( -/// param: Value, -/// info: &ApiMethod, -/// rpcenv: &mut dyn RpcEnvironment, -/// ) -> Result { -/// Ok(json!("Hello world!")) -/// } -/// -/// const API_METHOD_HELLO: ApiMethod = ApiMethod::new( -/// &ApiHandler::Sync(&hello), -/// &ObjectSchema::new("Hello World Example", &[]) -/// ); -/// ``` -pub type ApiHandlerFn = &'static (dyn Fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result - + Send - + Sync - + 'static); +#[cfg(any(feature = "router", feature = "cli"))] +#[doc(hidden)] +pub mod rpc_environment; -/// Asynchronous HTTP API handlers -/// -/// They get low level access to request and response data. Use this -/// to implement custom upload/download functions. -/// ``` -/// # use failure::*; -/// # use serde_json::{json, Value}; -/// # use proxmox_api::{*, schema::*}; -/// # -/// use futures::*; -/// use hyper::{Body, Response, http::request::Parts}; -/// -/// fn low_level_hello( -/// parts: Parts, -/// req_body: Body, -/// param: Value, -/// info: &ApiMethod, -/// rpcenv: Box, -/// ) -> ApiFuture { -/// async move { -/// let response = http::Response::builder() -/// .status(200) -/// .body(Body::from("Hello world!"))?; -/// Ok(response) -/// }.boxed() -/// } -/// -/// const API_METHOD_LOW_LEVEL_HELLO: ApiMethod = ApiMethod::new( -/// &ApiHandler::AsyncHttp(&low_level_hello), -/// &ObjectSchema::new("Hello World Example (low level)", &[]) -/// ); -/// ``` -pub type ApiAsyncHttpHandlerFn = &'static (dyn Fn(Parts, Body, Value, &'static ApiMethod, Box) -> ApiFuture - + Send - + Sync - + 'static); +#[cfg(any(feature = "router", feature = "cli"))] +#[doc(inline)] +pub use rpc_environment::{RpcEnvironment, RpcEnvironmentType}; -/// The output of an asynchronous API handler is a futrue yielding a `Response`. -pub type ApiFuture = Pin, failure::Error>> + Send>>; +#[cfg(feature = "router")] +pub mod format; -/// Enum for different types of API handler functions. -pub enum ApiHandler { - Sync(ApiHandlerFn), - AsyncHttp(ApiAsyncHttpHandlerFn), -} +#[cfg(feature = "router")] +#[doc(hidden)] +pub mod router; -const NULL_SCHEMA: Schema = Schema::Null; +#[cfg(feature = "router")] +#[doc(inline)] +pub use router::{ApiFuture, ApiHandler, ApiMethod, Router, SubRoute, SubdirMap}; -fn dummy_handler_fn( - _arg: Value, - _method: &ApiMethod, - _env: &mut dyn RpcEnvironment, -) -> Result { - // do nothing - Ok(Value::Null) -} - -const DUMMY_HANDLER: ApiHandler = ApiHandler::Sync(&dummy_handler_fn); - -/// This struct defines synchronous API call which returns the restulkt as json `Value` -pub struct ApiMethod { - /// The protected flag indicates that the provides function should be forwarded - /// to the deaemon running in priviledged mode. - pub protected: bool, - /// This flag indicates that the provided method may change the local timezone, so the server - /// should do a tzset afterwards - pub reload_timezone: bool, - /// Parameter type Schema - pub parameters: &'static schema::ObjectSchema, - /// Return type Schema - pub returns: &'static schema::Schema, - /// Handler function - pub handler: &'static ApiHandler, -} - -impl std::fmt::Debug for ApiMethod { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ApiMethod {{ ")?; - write!(f, " parameters: {:?}", self.parameters)?; - write!(f, " returns: {:?}", self.returns)?; - write!(f, " handler: {:p}", &self.handler)?; - write!(f, "}}") - } -} - -impl ApiMethod { - pub const fn new(handler: &'static ApiHandler, parameters: &'static ObjectSchema) -> Self { - Self { - parameters, - handler, - returns: &NULL_SCHEMA, - protected: false, - reload_timezone: false, - } - } - - pub const fn new_dummy(parameters: &'static ObjectSchema) -> Self { - Self { - parameters, - handler: &DUMMY_HANDLER, - returns: &NULL_SCHEMA, - protected: false, - reload_timezone: false, - } - } - - pub const fn returns(mut self, schema: &'static Schema) -> Self { - self.returns = schema; - - self - } - - pub const fn protected(mut self, protected: bool) -> Self { - self.protected = protected; - - self - } - - pub const fn reload_timezone(mut self, reload_timezone: bool) -> Self { - self.reload_timezone = reload_timezone; - - self - } -} +#[cfg(feature = "cli")] +pub mod cli; diff --git a/proxmox-api/src/router.rs b/proxmox-api/src/router.rs index 4daa296f..b1bf3a83 100644 --- a/proxmox-api/src/router.rs +++ b/proxmox-api/src/router.rs @@ -1,8 +1,88 @@ use std::collections::HashMap; +use std::fmt; +use std::future::Future; +use std::pin::Pin; -use http::Method; +use failure::Error; +use http::request::Parts; +use http::{Method, Response}; +use hyper::Body; +use serde_json::Value; -use crate::ApiMethod; +use crate::schema::{self, ObjectSchema, Schema}; +use crate::RpcEnvironment; + +/// A synchronous API handler gets a json Value as input and returns a json Value as output. +/// +/// Most API handler are synchronous. Use this to define such handler: +/// ``` +/// # use failure::*; +/// # use serde_json::{json, Value}; +/// # use proxmox_api::{*, schema::*}; +/// # +/// fn hello( +/// param: Value, +/// info: &ApiMethod, +/// rpcenv: &mut dyn RpcEnvironment, +/// ) -> Result { +/// Ok(json!("Hello world!")) +/// } +/// +/// const API_METHOD_HELLO: ApiMethod = ApiMethod::new( +/// &ApiHandler::Sync(&hello), +/// &ObjectSchema::new("Hello World Example", &[]) +/// ); +/// ``` +pub type ApiHandlerFn = &'static (dyn Fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result + + Send + + Sync + + 'static); + +/// Asynchronous HTTP API handlers +/// +/// They get low level access to request and response data. Use this +/// to implement custom upload/download functions. +/// ``` +/// # use failure::*; +/// # use serde_json::{json, Value}; +/// # use proxmox_api::{*, schema::*}; +/// # +/// use futures::*; +/// use hyper::{Body, Response, http::request::Parts}; +/// +/// fn low_level_hello( +/// parts: Parts, +/// req_body: Body, +/// param: Value, +/// info: &ApiMethod, +/// rpcenv: Box, +/// ) -> ApiFuture { +/// async move { +/// let response = http::Response::builder() +/// .status(200) +/// .body(Body::from("Hello world!"))?; +/// Ok(response) +/// }.boxed() +/// } +/// +/// const API_METHOD_LOW_LEVEL_HELLO: ApiMethod = ApiMethod::new( +/// &ApiHandler::AsyncHttp(&low_level_hello), +/// &ObjectSchema::new("Hello World Example (low level)", &[]) +/// ); +/// ``` +pub type ApiAsyncHttpHandlerFn = &'static (dyn Fn(Parts, Body, Value, &'static ApiMethod, Box) -> ApiFuture + + Send + + Sync + + 'static); + +/// The output of an asynchronous API handler is a futrue yielding a `Response`. +pub type ApiFuture = Pin, failure::Error>> + Send>>; + +/// Enum for different types of API handler functions. +pub enum ApiHandler { + Sync(ApiHandlerFn), + AsyncHttp(ApiAsyncHttpHandlerFn), +} /// Lookup table to child `Router`s /// @@ -221,7 +301,87 @@ impl Router { } impl Default for Router { + #[inline] fn default() -> Self { Self::new() } } + +const NULL_SCHEMA: Schema = Schema::Null; + +fn dummy_handler_fn( + _arg: Value, + _method: &ApiMethod, + _env: &mut dyn RpcEnvironment, +) -> Result { + // do nothing + Ok(Value::Null) +} + +const DUMMY_HANDLER: ApiHandler = ApiHandler::Sync(&dummy_handler_fn); + +/// This struct defines synchronous API call which returns the restulkt as json `Value` +pub struct ApiMethod { + /// The protected flag indicates that the provides function should be forwarded + /// to the deaemon running in priviledged mode. + pub protected: bool, + /// This flag indicates that the provided method may change the local timezone, so the server + /// should do a tzset afterwards + pub reload_timezone: bool, + /// Parameter type Schema + pub parameters: &'static schema::ObjectSchema, + /// Return type Schema + pub returns: &'static schema::Schema, + /// Handler function + pub handler: &'static ApiHandler, +} + +impl std::fmt::Debug for ApiMethod { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ApiMethod {{ ")?; + write!(f, " parameters: {:?}", self.parameters)?; + write!(f, " returns: {:?}", self.returns)?; + write!(f, " handler: {:p}", &self.handler)?; + write!(f, "}}") + } +} + +impl ApiMethod { + pub const fn new(handler: &'static ApiHandler, parameters: &'static ObjectSchema) -> Self { + Self { + parameters, + handler, + returns: &NULL_SCHEMA, + protected: false, + reload_timezone: false, + } + } + + pub const fn new_dummy(parameters: &'static ObjectSchema) -> Self { + Self { + parameters, + handler: &DUMMY_HANDLER, + returns: &NULL_SCHEMA, + protected: false, + reload_timezone: false, + } + } + + pub const fn returns(mut self, schema: &'static Schema) -> Self { + self.returns = schema; + + self + } + + pub const fn protected(mut self, protected: bool) -> Self { + self.protected = protected; + + self + } + + pub const fn reload_timezone(mut self, reload_timezone: bool) -> Self { + self.reload_timezone = reload_timezone; + + self + } +}