3 use crate::api_schema
::*;
4 use serde_json
::{Value}
;
5 use std
::collections
::HashMap
;
9 use hyper
::{Body, Response, StatusCode}
;
10 use hyper
::rt
::Future
;
11 use hyper
::http
::request
::Parts
;
13 pub type BoxFut
= Box
<Future
<Item
= Response
<Body
>, Error
= failure
::Error
> + Send
>;
15 /// Abstract Interface for API methods to interact with the environment
16 pub trait RpcEnvironment
{
18 /// Use this to pass additional result data. It is up to the environment
19 /// how the data is used.
20 fn set_result_attrib(&mut self, name
: &str, value
: Value
);
22 /// Query additional result data.
23 fn get_result_attrib(&self, name
: &str) -> Option
<&Value
>;
25 /// The environment type
26 fn env_type(&self) -> RpcEnvironmentType
;
29 fn set_user(&mut self, user
: Option
<String
>);
32 fn get_user(&self) -> Option
<String
>;
38 /// We use this to enumerate the different environment types. Some methods
39 /// needs to do different things when started from the command line interface,
40 /// or when executed from a privileged server running as root.
41 #[derive(PartialEq, Copy, Clone)]
42 pub enum RpcEnvironmentType
{
43 /// Command started from command line
45 /// Access from public accessible server
47 /// Access from privileged server (run as root)
51 #[derive(Debug, Fail)]
52 pub struct HttpError
{
58 pub fn new(code
: StatusCode
, message
: String
) -> Self {
59 HttpError { code, message }
63 impl fmt
::Display
for HttpError
{
64 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
65 write
!(f
, "Error {}: {}", self.code
, self.message
)
69 macro_rules
! http_err
{
70 ($status
:ident
, $msg
:expr
) => {{
71 Error
::from(HttpError
::new(StatusCode
::$status
, $msg
))
75 type ApiHandlerFn
= fn(Value
, &ApiMethod
, &mut dyn RpcEnvironment
) -> Result
<Value
, Error
>;
77 type ApiAsyncHandlerFn
= fn(Parts
, Body
, Value
, &ApiAsyncMethod
, &mut dyn RpcEnvironment
) -> Result
<BoxFut
, Error
>;
79 /// This struct defines synchronous API call which returns the restulkt as json `Value`
80 pub struct ApiMethod
{
81 /// The protected flag indicates that the provides function should be forwarded
82 /// to the deaemon running in priviledged mode.
84 /// This flag indicates that the provided method may change the local timezone, so the server
85 /// should do a tzset afterwards
86 pub reload_timezone
: bool
,
87 /// Parameter type Schema
88 pub parameters
: ObjectSchema
,
89 /// Return type Schema
90 pub returns
: Arc
<Schema
>,
92 pub handler
: ApiHandlerFn
,
97 pub fn new(handler
: ApiHandlerFn
, parameters
: ObjectSchema
) -> Self {
101 returns
: Arc
::new(Schema
::Null
),
103 reload_timezone
: false,
107 pub fn returns
<S
: Into
<Arc
<Schema
>>>(mut self, schema
: S
) -> Self {
109 self.returns
= schema
.into();
114 pub fn protected(mut self, protected
: bool
) -> Self {
116 self.protected
= protected
;
121 pub fn reload_timezone(mut self, reload_timezone
: bool
) -> Self {
123 self.reload_timezone
= reload_timezone
;
129 pub struct ApiAsyncMethod
{
130 pub parameters
: ObjectSchema
,
131 pub returns
: Arc
<Schema
>,
132 pub handler
: ApiAsyncHandlerFn
,
135 impl ApiAsyncMethod
{
137 pub fn new(handler
: ApiAsyncHandlerFn
, parameters
: ObjectSchema
) -> Self {
141 returns
: Arc
::new(Schema
::Null
),
145 pub fn returns
<S
: Into
<Arc
<Schema
>>>(mut self, schema
: S
) -> Self {
147 self.returns
= schema
.into();
155 Hash(HashMap
<String
, Router
>),
156 MatchAll { router: Box<Router>, param_name: String }
,
159 pub enum MethodDefinition
{
162 Async(ApiAsyncMethod
),
166 pub get
: MethodDefinition
,
167 pub put
: MethodDefinition
,
168 pub post
: MethodDefinition
,
169 pub delete
: MethodDefinition
,
170 pub subroute
: SubRoute
,
175 pub fn new() -> Self {
177 get
: MethodDefinition
::None
,
178 put
: MethodDefinition
::None
,
179 post
: MethodDefinition
::None
,
180 delete
: MethodDefinition
::None
,
181 subroute
: SubRoute
::None
185 pub fn subdir
<S
: Into
<String
>>(mut self, subdir
: S
, router
: Router
) -> Self {
186 if let SubRoute
::None
= self.subroute
{
187 self.subroute
= SubRoute
::Hash(HashMap
::new());
189 match self.subroute
{
190 SubRoute
::Hash(ref mut map
) => {
191 map
.insert(subdir
.into(), router
);
193 _
=> panic
!("unexpected subroute type"),
198 pub fn subdirs(mut self, map
: HashMap
<String
, Router
>) -> Self {
199 self.subroute
= SubRoute
::Hash(map
);
203 pub fn match_all
<S
: Into
<String
>>(mut self, param_name
: S
, router
: Router
) -> Self {
204 if let SubRoute
::None
= self.subroute
{
205 self.subroute
= SubRoute
::MatchAll { router: Box::new(router), param_name: param_name.into() }
;
207 panic
!("unexpected subroute type");
212 pub fn get(mut self, m
: ApiMethod
) -> Self {
213 self.get
= MethodDefinition
::Simple(m
);
217 pub fn put(mut self, m
: ApiMethod
) -> Self {
218 self.put
= MethodDefinition
::Simple(m
);
222 pub fn post(mut self, m
: ApiMethod
) -> Self {
223 self.post
= MethodDefinition
::Simple(m
);
227 pub fn upload(mut self, m
: ApiAsyncMethod
) -> Self {
228 self.post
= MethodDefinition
::Async(m
);
232 pub fn download(mut self, m
: ApiAsyncMethod
) -> Self {
233 self.get
= MethodDefinition
::Async(m
);
238 pub fn delete(mut self, m
: ApiMethod
) -> Self {
239 self.delete
= MethodDefinition
::Simple(m
);
243 pub fn find_route(&self, components
: &[&str], uri_param
: &mut HashMap
<String
, String
>) -> Option
<&Router
> {
245 if components
.len() == 0 { return Some(self); }
;
247 let (dir
, rest
) = (components
[0], &components
[1..]);
249 match self.subroute
{
250 SubRoute
::None
=> {}
,
251 SubRoute
::Hash(ref dirmap
) => {
252 if let Some(ref router
) = dirmap
.get(dir
) {
253 println
!("FOUND SUBDIR {}", dir
);
254 return router
.find_route(rest
, uri_param
);
257 SubRoute
::MatchAll { ref router, ref param_name }
=> {
258 println
!("URI PARAM {} = {}", param_name
, dir
); // fixme: store somewhere
259 uri_param
.insert(param_name
.clone(), dir
.into());
260 return router
.find_route(rest
, uri_param
);