use std::{collections::HashMap, str::FromStr};
use bytes::Bytes;
use http::{Extensions, Method};
use http_body::Limited;
use hyper::Body;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::error::ApiError;
#[derive(Debug)]
pub struct Request {
pub id: Uuid,
pub operation: Operation,
pub path: String,
pub namespace: Vec<String>,
pub data: Bytes,
pub query_string: String,
pub extensions: http::Extensions,
pub params: Vec<String>,
pub token: Option<String>,
pub headers: HashMap<String, String>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Operation {
Create,
Read,
Update,
Delete,
Revoke,
Renew,
}
impl FromStr for Operation {
type Err = ApiError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &s.to_lowercase()[..] {
"create" => Ok(Self::Create),
"read" => Ok(Self::Read),
"update" => Ok(Self::Update),
"delete" => Ok(Self::Delete),
"revoke" => Ok(Self::Revoke),
"renew" => Ok(Self::Renew),
_ => Err(ApiError::bad_request()),
}
}
}
impl Request {
pub async fn new(raw: hyper::Request<Limited<Body>>) -> Result<Self, ApiError> {
let uri = raw.uri().clone();
let token = raw
.headers()
.get("X-Covert-Token")
.map(|val| val.to_str().unwrap_or_default())
.and_then(|token| {
if token.is_empty() {
None
} else {
Some(token.to_string())
}
});
let namespace = raw
.headers()
.get("X-Covert-Namespace")
.and_then(|namespace| namespace.to_str().ok())
.map_or_else(
|| vec!["root".to_string()],
|namespace| {
namespace
.trim()
.to_lowercase()
.split('/')
.map(ToString::to_string)
.filter(|ns| !ns.is_empty())
.collect::<Vec<_>>()
},
);
let headers = raw
.headers()
.iter()
.map(|(name, value)| {
(
name.to_string(),
value.to_str().unwrap_or_default().to_string(),
)
})
.collect();
let operation = match *raw.method() {
Method::GET => Operation::Read,
Method::POST => Operation::Create,
Method::PUT => Operation::Update,
Method::DELETE => Operation::Delete,
_ => return Err(ApiError::bad_request()),
};
let bytes = hyper::body::to_bytes(raw.into_body())
.await
.map_err(|_| ApiError::bad_request())?;
let mut path = uri.path();
if path.starts_with("/v1/") {
path = &path[4..];
}
Ok(Self {
id: Uuid::new_v4(),
operation,
namespace,
query_string: uri.query().unwrap_or_default().to_string(),
path: path.to_string(),
extensions: Extensions::new(),
token,
params: vec![],
data: bytes,
headers,
})
}
pub fn operation(&self) -> Operation {
self.operation
}
pub fn advance_path(&mut self, prefix: &str) -> bool {
if !self.path.starts_with(prefix) {
return false;
}
self.path = self.path[prefix.len()..].to_string();
true
}
}