Skip to main content

cloudillo_types/
extract.rs

1//! Custom Axum extractors for Cloudillo-specific types.
2//!
3//! Provides `FromRequestParts` implementations for `TnId` and `IdTag`
4//! that work with any state implementing the required traits.
5
6use std::sync::Arc;
7
8use async_trait::async_trait;
9use axum::extract::FromRequestParts;
10use axum::http::request::Parts;
11
12use crate::error::Error;
13use crate::types::TnId;
14
15// IdTag //
16//*******//
17/// Identity tag extracted from request extensions (set by auth middleware).
18#[derive(Clone, Debug)]
19pub struct IdTag(pub Box<str>);
20
21impl IdTag {
22	pub fn new(id_tag: &str) -> IdTag {
23		IdTag(Box::from(id_tag))
24	}
25}
26
27impl<S> FromRequestParts<S> for IdTag
28where
29	S: Send + Sync,
30{
31	type Rejection = Error;
32
33	async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
34		if let Some(id_tag) = parts.extensions.get::<IdTag>().cloned() {
35			Ok(id_tag)
36		} else {
37			Err(Error::PermissionDenied)
38		}
39	}
40}
41
42// TnId //
43//******//
44/// Trait for resolving `TnId` from an identity tag string.
45///
46/// Implement this on your application state type to enable the
47/// `TnId` Axum extractor.
48#[async_trait]
49pub trait TnIdResolver: Send + Sync {
50	async fn resolve_tn_id(&self, id_tag: &str) -> Result<TnId, Error>;
51}
52
53/// Blanket impl for `Arc<T>` so that `App = Arc<AppState>` works
54/// when `AppState` implements `TnIdResolver`.
55#[async_trait]
56impl<T: TnIdResolver> TnIdResolver for Arc<T> {
57	async fn resolve_tn_id(&self, id_tag: &str) -> Result<TnId, Error> {
58		(**self).resolve_tn_id(id_tag).await
59	}
60}
61
62impl<S> FromRequestParts<S> for TnId
63where
64	S: TnIdResolver + Send + Sync,
65{
66	type Rejection = Error;
67
68	async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
69		if let Some(id_tag) = parts.extensions.get::<IdTag>().cloned() {
70			state.resolve_tn_id(&id_tag.0).await.map_err(|_| Error::PermissionDenied)
71		} else {
72			Err(Error::PermissionDenied)
73		}
74	}
75}
76
77// vim: ts=4