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
>;
35 #[derive(PartialEq, Copy, Clone)]
36 pub enum RpcEnvironmentType
{
37 /// command started from command line
39 /// access from public acessable server
41 /// ... access from priviledged server (run as root)
45 #[derive(Debug, Fail)]
46 pub struct HttpError
{
52 pub fn new(code
: StatusCode
, message
: String
) -> Self {
53 HttpError { code, message }
57 impl fmt
::Display
for HttpError
{
58 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
59 write
!(f
, "Error {}: {}", self.code
, self.message
)
63 macro_rules
! http_err
{
64 ($status
:ident
, $msg
:expr
) => {{
65 Error
::from(HttpError
::new(StatusCode
::$status
, $msg
))
69 type ApiHandlerFn
= fn(Value
, &ApiMethod
, &mut dyn RpcEnvironment
) -> Result
<Value
, Error
>;
71 type ApiAsyncHandlerFn
= fn(Parts
, Body
, Value
, &ApiAsyncMethod
, &mut dyn RpcEnvironment
) -> Result
<BoxFut
, Error
>;
73 /// This struct defines synchronous API call which returns the restulkt as json `Value`
74 pub struct ApiMethod
{
75 /// The protected flag indicates that the provides function should be forwarded
76 /// to the deaemon running in priviledged mode.
78 /// This flag indicates that the provided method may change the local timezone, so the server
79 /// should do a tzset afterwards
80 pub reload_timezone
: bool
,
81 /// Parameter type Schema
82 pub parameters
: ObjectSchema
,
83 /// Return type Schema
84 pub returns
: Arc
<Schema
>,
86 pub handler
: ApiHandlerFn
,
91 pub fn new(handler
: ApiHandlerFn
, parameters
: ObjectSchema
) -> Self {
95 returns
: Arc
::new(Schema
::Null
),
97 reload_timezone
: false,
101 pub fn returns
<S
: Into
<Arc
<Schema
>>>(mut self, schema
: S
) -> Self {
103 self.returns
= schema
.into();
108 pub fn protected(mut self, protected
: bool
) -> Self {
110 self.protected
= protected
;
115 pub fn reload_timezone(mut self, reload_timezone
: bool
) -> Self {
117 self.reload_timezone
= reload_timezone
;
123 pub struct ApiAsyncMethod
{
124 pub parameters
: ObjectSchema
,
125 pub returns
: Arc
<Schema
>,
126 pub handler
: ApiAsyncHandlerFn
,
129 impl ApiAsyncMethod
{
131 pub fn new(handler
: ApiAsyncHandlerFn
, parameters
: ObjectSchema
) -> Self {
135 returns
: Arc
::new(Schema
::Null
),
139 pub fn returns
<S
: Into
<Arc
<Schema
>>>(mut self, schema
: S
) -> Self {
141 self.returns
= schema
.into();
149 Hash(HashMap
<String
, Router
>),
150 MatchAll { router: Box<Router>, param_name: String }
,
153 pub enum MethodDefinition
{
156 Async(ApiAsyncMethod
),
160 pub get
: MethodDefinition
,
161 pub put
: MethodDefinition
,
162 pub post
: MethodDefinition
,
163 pub delete
: MethodDefinition
,
164 pub subroute
: SubRoute
,
169 pub fn new() -> Self {
171 get
: MethodDefinition
::None
,
172 put
: MethodDefinition
::None
,
173 post
: MethodDefinition
::None
,
174 delete
: MethodDefinition
::None
,
175 subroute
: SubRoute
::None
179 pub fn subdir
<S
: Into
<String
>>(mut self, subdir
: S
, router
: Router
) -> Self {
180 if let SubRoute
::None
= self.subroute
{
181 self.subroute
= SubRoute
::Hash(HashMap
::new());
183 match self.subroute
{
184 SubRoute
::Hash(ref mut map
) => {
185 map
.insert(subdir
.into(), router
);
187 _
=> panic
!("unexpected subroute type"),
192 pub fn subdirs(mut self, map
: HashMap
<String
, Router
>) -> Self {
193 self.subroute
= SubRoute
::Hash(map
);
197 pub fn match_all
<S
: Into
<String
>>(mut self, param_name
: S
, router
: Router
) -> Self {
198 if let SubRoute
::None
= self.subroute
{
199 self.subroute
= SubRoute
::MatchAll { router: Box::new(router), param_name: param_name.into() }
;
201 panic
!("unexpected subroute type");
206 pub fn get(mut self, m
: ApiMethod
) -> Self {
207 self.get
= MethodDefinition
::Simple(m
);
211 pub fn put(mut self, m
: ApiMethod
) -> Self {
212 self.put
= MethodDefinition
::Simple(m
);
216 pub fn post(mut self, m
: ApiMethod
) -> Self {
217 self.post
= MethodDefinition
::Simple(m
);
221 pub fn upload(mut self, m
: ApiAsyncMethod
) -> Self {
222 self.post
= MethodDefinition
::Async(m
);
226 pub fn download(mut self, m
: ApiAsyncMethod
) -> Self {
227 self.get
= MethodDefinition
::Async(m
);
232 pub fn delete(mut self, m
: ApiMethod
) -> Self {
233 self.delete
= MethodDefinition
::Simple(m
);
237 pub fn find_route(&self, components
: &[&str], uri_param
: &mut HashMap
<String
, String
>) -> Option
<&Router
> {
239 if components
.len() == 0 { return Some(self); }
;
241 let (dir
, rest
) = (components
[0], &components
[1..]);
243 match self.subroute
{
244 SubRoute
::None
=> {}
,
245 SubRoute
::Hash(ref dirmap
) => {
246 if let Some(ref router
) = dirmap
.get(dir
) {
247 println
!("FOUND SUBDIR {}", dir
);
248 return router
.find_route(rest
, uri_param
);
251 SubRoute
::MatchAll { ref router, ref param_name }
=> {
252 println
!("URI PARAM {} = {}", param_name
, dir
); // fixme: store somewhere
253 uri_param
.insert(param_name
.clone(), dir
.into());
254 return router
.find_route(rest
, uri_param
);