1use std::{collections::HashMap, str::FromStr};
2
3use bytes::Bytes;
4use http::{Extensions, Method};
5use http_body::Limited;
6use hyper::Body;
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10use crate::error::ApiError;
11
12#[derive(Debug)]
13pub struct Request {
14 pub id: Uuid,
15
16 pub operation: Operation,
17
18 pub path: String,
19
20 pub namespace: Vec<String>,
21
22 pub data: Bytes,
23 pub query_string: String,
24 pub extensions: http::Extensions,
26 pub params: Vec<String>,
27 pub token: Option<String>,
28
29 pub headers: HashMap<String, String>,
30}
31
32#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
35pub enum Operation {
36 Create,
38 Read,
39 Update,
40 Delete,
41 Revoke,
43 Renew,
44}
45
46impl FromStr for Operation {
47 type Err = ApiError;
48
49 fn from_str(s: &str) -> Result<Self, Self::Err> {
50 match &s.to_lowercase()[..] {
51 "create" => Ok(Self::Create),
52 "read" => Ok(Self::Read),
53 "update" => Ok(Self::Update),
54 "delete" => Ok(Self::Delete),
55 "revoke" => Ok(Self::Revoke),
56 "renew" => Ok(Self::Renew),
57 _ => Err(ApiError::bad_request()),
58 }
59 }
60}
61
62impl Request {
63 pub async fn new(raw: hyper::Request<Limited<Body>>) -> Result<Self, ApiError> {
70 let uri = raw.uri().clone();
71 let token = raw
72 .headers()
73 .get("X-Covert-Token")
74 .map(|val| val.to_str().unwrap_or_default())
75 .and_then(|token| {
76 if token.is_empty() {
77 None
78 } else {
79 Some(token.to_string())
80 }
81 });
82 let namespace = raw
83 .headers()
84 .get("X-Covert-Namespace")
85 .and_then(|namespace| namespace.to_str().ok())
86 .map_or_else(
87 || vec!["root".to_string()],
88 |namespace| {
89 namespace
90 .trim()
91 .to_lowercase()
92 .split('/')
93 .map(ToString::to_string)
94 .filter(|ns| !ns.is_empty())
95 .collect::<Vec<_>>()
96 },
97 );
98 let headers = raw
99 .headers()
100 .iter()
101 .map(|(name, value)| {
102 (
103 name.to_string(),
104 value.to_str().unwrap_or_default().to_string(),
105 )
106 })
107 .collect();
108
109 let operation = match *raw.method() {
110 Method::GET => Operation::Read,
111 Method::POST => Operation::Create,
112 Method::PUT => Operation::Update,
113 Method::DELETE => Operation::Delete,
114 _ => return Err(ApiError::bad_request()),
115 };
116
117 let bytes = hyper::body::to_bytes(raw.into_body())
118 .await
119 .map_err(|_| ApiError::bad_request())?;
120
121 let mut path = uri.path();
122 if path.starts_with("/v1/") {
123 path = &path[4..];
124 }
125
126 Ok(Self {
127 id: Uuid::new_v4(),
128 operation,
129 namespace,
130 query_string: uri.query().unwrap_or_default().to_string(),
131 path: path.to_string(),
132 extensions: Extensions::new(),
133 token,
134 params: vec![],
135 data: bytes,
136 headers,
137 })
138 }
139
140 pub fn operation(&self) -> Operation {
141 self.operation
142 }
143
144 pub fn advance_path(&mut self, prefix: &str) -> bool {
145 if !self.path.starts_with(prefix) {
146 return false;
147 }
148
149 self.path = self.path[prefix.len()..].to_string();
150
151 true
152 }
153}