1#![deny(warnings)]
2
3mod error;
4
5pub use error::Error;
6
7use actix_http::Method;
8use actix_web::{
9 cookie::Cookie,
10 dev::{AppService, HttpServiceFactory},
11 http::header::{self, HeaderName, HeaderValue, TryIntoHeaderValue},
12 FromRequest, HttpRequest, HttpResponse, Responder, Route, Scope,
13};
14use serde::Serialize;
15use std::collections::HashMap;
16
17use actix_http::body::BoxBody;
18use actix_web::dev::Handler;
19pub use actix_web::http::StatusCode;
20use std::future::Future;
21
22#[derive(Debug)]
24pub enum ContentType {
25 Json,
26 FormData,
27 }
29
30impl ToString for ContentType {
31 fn to_string(&self) -> String {
32 match self {
33 ContentType::Json => "application/json".to_string(),
34 ContentType::FormData => "application/x-www-form-urlencoded".to_string(),
36 }
37 }
38}
39
40pub struct Answer<'a, T> {
42 response: T,
43 status_code: Option<StatusCode>,
44 cookies: Vec<Cookie<'a>>,
45 headers: HashMap<String, HeaderValue>,
46 content_type: Option<ContentType>,
47}
48
49impl<'a, T: Serialize> Answer<'a, T> {
50 pub fn new(response: T) -> Answer<'a, T> {
51 Answer {
52 response,
53 status_code: None,
54 cookies: vec![],
55 headers: HashMap::new(),
56 content_type: None,
57 }
58 }
59
60 pub fn header<V>(mut self, key: String, value: V) -> Self
62 where
63 V: TryIntoHeaderValue,
64 {
65 if let Ok(value) = value.try_into_value() {
66 self.headers.insert(key, value);
67 }
68
69 self
70 }
71
72 pub fn cookie(mut self, cookie: Cookie<'a>) -> Self {
74 self.cookies.push(cookie);
75
76 self
77 }
78
79 pub fn status(mut self, status: StatusCode) -> Self {
81 self.status_code = Some(status);
82
83 self
84 }
85
86 pub fn content_type(mut self, content_type: Option<ContentType>) -> Self {
89 self.content_type = content_type;
90
91 self
92 }
93
94 pub fn to_string(&self) -> Result<String, Error> {
96 match self.content_type {
97 Some(ContentType::Json) => Ok(serde_json::to_string(&self.response)?),
98 Some(ContentType::FormData) => Ok(serde_urlencoded::to_string(&self.response)?),
99 _ => Ok("".to_owned()),
101 }
102 }
103}
104
105impl<'a, T: Serialize> Responder for Answer<'a, T> {
106 type Body = BoxBody;
107
108 fn respond_to(self, _: &HttpRequest) -> HttpResponse {
109 let body = match self.to_string() {
110 Ok(body) => body,
111 Err(e) => return HttpResponse::from_error(e),
112 };
113
114 let mut response = &mut HttpResponse::build(self.status_code.unwrap_or(StatusCode::OK));
115
116 if let Some(content_type) = self.content_type {
117 response = response.append_header((header::CONTENT_TYPE, content_type.to_string()));
118 }
119
120 for (name, value) in self.headers {
121 if let Ok(header_name) = name.parse::<HeaderName>() {
122 response = response.append_header((header_name, value))
123 }
124 }
125
126 for cookie in self.cookies {
127 response = response.cookie(cookie);
128 }
129
130 response.body(body)
131 }
132}
133
134pub struct Api {
138 root: Scope,
139 resources: HashMap<String, Route>,
140}
141
142impl Default for Api {
143 fn default() -> Self {
144 Self::new()
145 }
146}
147
148impl Api {
149 pub fn new() -> Self {
150 Api {
151 root: Scope::new(""),
152 resources: HashMap::new(),
153 }
154 }
155
156 pub fn bind<T, F, R>(mut self, path: &str, method: Method, handler: F) -> Self
158 where
159 T: FromRequest + 'static,
160 R: Future + 'static,
161 R::Output: Responder + 'static,
162 F: Handler<T, Future = R>,
163 F::Output: Responder + 'static,
164 {
165 take_mut::take(
166 self.resources
167 .entry(path.to_owned())
168 .or_insert_with(Route::new),
169 |route| route.method(method).to(handler),
170 );
171
172 self
173 }
174}
175
176impl HttpServiceFactory for Api {
177 fn register(mut self, config: &mut AppService) {
178 let keys: Vec<String> = self.resources.keys().cloned().collect();
179
180 for key in keys.iter() {
181 if let Some(resource) = self.resources.remove(key) {
182 self.root = self.root.route(key, resource);
183 }
184 }
185
186 self.root.register(config);
187 }
188}