api: add 'router' and 'cli' features
Move API types into the router module and make router and cli optional. This way the 'router' module is the only thing with a dependency on 'hyper', allowing the use of the schema alone without pulling in the entire hyper stack. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
2d12f81235
commit
d00a096851
@ -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" ]
|
||||
|
@ -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 {
|
||||
|
@ -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<Value, Error> {
|
||||
/// 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<Value, Error>
|
||||
+ 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<dyn RpcEnvironment>,
|
||||
/// ) -> 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<dyn RpcEnvironment>) -> 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<Box<dyn Future<Output = Result<Response<Body>, 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<Value, Error> {
|
||||
// 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;
|
||||
|
@ -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<Value, Error> {
|
||||
/// 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<Value, Error>
|
||||
+ 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<dyn RpcEnvironment>,
|
||||
/// ) -> 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<dyn RpcEnvironment>) -> ApiFuture
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static);
|
||||
|
||||
/// The output of an asynchronous API handler is a futrue yielding a `Response`.
|
||||
pub type ApiFuture = Pin<Box<dyn Future<Output = Result<Response<Body>, 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<Value, Error> {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user