Skip to main content

wae_effect/
lib.rs

1//! 代数效应系统
2//!
3//! 提供声明式的依赖注入能力,允许在处理请求时通过类型安全的接口获取各种依赖。
4
5#![warn(missing_docs)]
6
7use std::{collections::HashMap, fmt, sync::Arc};
8
9use axum::{
10    body::Body,
11    extract::FromRequestParts,
12    http::{StatusCode, request::Parts},
13    response::{IntoResponse, Response},
14};
15
16/// 依赖容器
17///
18/// 存储所有注册的服务实例。
19#[derive(Default)]
20pub struct Dependencies {
21    services: HashMap<String, Box<dyn std::any::Any + Send + Sync>>,
22}
23
24impl Dependencies {
25    /// 创建新的依赖容器
26    pub fn new() -> Self {
27        Self { services: HashMap::new() }
28    }
29
30    /// 注册服务
31    pub fn register<T: Send + Sync + 'static>(&mut self, name: &str, service: T) {
32        self.services.insert(name.to_string(), Box::new(service));
33    }
34
35    /// 获取服务
36    pub fn get<T: Clone + Send + Sync + 'static>(&self, name: &str) -> Result<T, EffectError> {
37        self.services
38            .get(name)
39            .and_then(|s| s.downcast_ref::<T>())
40            .cloned()
41            .ok_or_else(|| EffectError::NotFound(name.to_string()))
42    }
43}
44
45/// 代数效应请求上下文
46///
47/// 包含依赖容器和请求信息,用于在请求处理过程中获取依赖。
48pub struct Effectful {
49    deps: Arc<Dependencies>,
50    parts: Parts,
51}
52
53impl Effectful {
54    /// 创建新的 Effectful
55    pub fn new(deps: Arc<Dependencies>, parts: Parts) -> Self {
56        Self { deps, parts }
57    }
58
59    /// 获取依赖
60    pub fn get<T: Clone + Send + Sync + 'static>(&self, name: &str) -> Result<T, EffectError> {
61        self.deps.get(name)
62    }
63
64    /// 获取请求头
65    pub fn header(&self, name: &str) -> Option<&str> {
66        self.parts.headers.get(name).and_then(|v| v.to_str().ok())
67    }
68}
69
70/// 从请求中提取 Effectful
71impl<S> FromRequestParts<S> for Effectful
72where
73    S: Send + Sync,
74{
75    type Rejection = Response<Body>;
76
77    fn from_request_parts(
78        parts: &mut Parts,
79        _state: &S,
80    ) -> impl std::future::Future<Output = Result<Self, Self::Rejection>> + Send {
81        let deps = Arc::new(Dependencies::new());
82        async move { Ok(Effectful::new(deps, parts.clone())) }
83    }
84}
85
86/// 代数效应构建器
87///
88/// 用于构建依赖容器并注册各种依赖。
89pub struct AlgebraicEffect {
90    deps: Dependencies,
91}
92
93impl Default for AlgebraicEffect {
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl AlgebraicEffect {
100    /// 创建新的代数效应构建器
101    pub fn new() -> Self {
102        Self { deps: Dependencies::new() }
103    }
104
105    /// 注册服务
106    pub fn with<T: Send + Sync + 'static>(mut self, name: &str, service: T) -> Self {
107        self.deps.register(name, service);
108        self
109    }
110
111    /// 构建依赖容器
112    pub fn build(self) -> Arc<Dependencies> {
113        Arc::new(self.deps)
114    }
115}
116
117/// 效应错误类型
118#[derive(Debug)]
119pub enum EffectError {
120    /// 依赖未找到
121    NotFound(String),
122
123    /// 类型转换失败
124    TypeMismatch {
125        /// 依赖名称
126        name: String,
127        /// 期望类型
128        expected: String,
129        /// 实际类型
130        actual: String,
131    },
132
133    /// 配置加载失败
134    ConfigError(wae_config::ConfigError),
135
136    /// 数据库错误
137    DatabaseError(String),
138}
139
140impl fmt::Display for EffectError {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        match self {
143            EffectError::NotFound(msg) => write!(f, "Dependency not found: {}", msg),
144            EffectError::TypeMismatch { name, expected, actual } => {
145                write!(f, "Type mismatch for {}: expected {}, got {}", name, expected, actual)
146            }
147            EffectError::ConfigError(err) => write!(f, "Config error: {}", err),
148            EffectError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
149        }
150    }
151}
152
153impl std::error::Error for EffectError {}
154
155impl From<wae_config::ConfigError> for EffectError {
156    fn from(err: wae_config::ConfigError) -> Self {
157        EffectError::ConfigError(err)
158    }
159}
160
161impl IntoResponse for EffectError {
162    fn into_response(self) -> Response {
163        let (status, message) = match &self {
164            EffectError::NotFound(_) => (StatusCode::NOT_FOUND, self.to_string()),
165            EffectError::TypeMismatch { .. } => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
166            EffectError::ConfigError(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
167            EffectError::DatabaseError(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
168        };
169        (status, message).into_response()
170    }
171}