use std::collections::HashMap;
use http::{HeaderMap, Method, Uri};
use oxihttp_core::{Header, OxiHttpError};
pub struct RequestParts<'a> {
pub method: &'a Method,
pub uri: &'a Uri,
pub headers: &'a HeaderMap,
pub path_params: &'a HashMap<String, String>,
}
pub trait FromRequestParts: Sized {
type Rejection: Into<OxiHttpError>;
fn from_request_parts(parts: &RequestParts<'_>) -> Result<Self, Self::Rejection>;
}
pub struct TypedHeader<H: Header>(pub H);
impl<H: Header> FromRequestParts for TypedHeader<H> {
type Rejection = OxiHttpError;
fn from_request_parts(parts: &RequestParts<'_>) -> Result<Self, Self::Rejection> {
H::decode(parts.headers).map(TypedHeader)
}
}
#[cfg(test)]
mod tests {
use super::*;
use http::HeaderValue;
use oxihttp_core::{Authorization, ContentType, Host};
fn make_parts<'a>(
method: &'a Method,
uri: &'a Uri,
headers: &'a HeaderMap,
path_params: &'a HashMap<String, String>,
) -> RequestParts<'a> {
RequestParts {
method,
uri,
headers,
path_params,
}
}
#[test]
fn test_typed_header_extraction_via_request_parts() {
let method = Method::POST;
let uri: Uri = "/upload".parse().expect("parse uri");
let mut headers = HeaderMap::new();
headers.insert(
http::header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
let path_params = HashMap::new();
let parts = make_parts(&method, &uri, &headers, &path_params);
let TypedHeader(ct) =
TypedHeader::<ContentType>::from_request_parts(&parts).expect("should extract");
assert_eq!(ct, ContentType::Json);
}
#[test]
fn test_typed_header_missing_returns_error() {
let method = Method::POST;
let uri: Uri = "/upload".parse().expect("parse uri");
let headers = HeaderMap::new();
let path_params = HashMap::new();
let parts = make_parts(&method, &uri, &headers, &path_params);
let result = TypedHeader::<ContentType>::from_request_parts(&parts);
assert!(result.is_err(), "should fail when Content-Type is absent");
}
#[test]
fn test_typed_header_host_ok() {
let method = Method::GET;
let uri: Uri = "/".parse().expect("parse uri");
let mut headers = HeaderMap::new();
headers.insert(http::header::HOST, HeaderValue::from_static("example.com"));
let path_params = HashMap::new();
let parts = make_parts(&method, &uri, &headers, &path_params);
let TypedHeader(host) =
TypedHeader::<Host>::from_request_parts(&parts).expect("should extract");
assert_eq!(host, Host("example.com".to_string()));
}
#[test]
fn test_typed_header_authorization_ok() {
let method = Method::GET;
let uri: Uri = "/secure".parse().expect("parse uri");
let mut headers = HeaderMap::new();
headers.insert(
http::header::AUTHORIZATION,
HeaderValue::from_static("Bearer secret-token"),
);
let path_params = HashMap::new();
let parts = make_parts(&method, &uri, &headers, &path_params);
let TypedHeader(auth) =
TypedHeader::<Authorization>::from_request_parts(&parts).expect("should extract");
assert_eq!(auth, Authorization("Bearer secret-token".to_string()));
}
}