Skip to main content

rust_patterns/
factory.rs

1use std::{any::TypeId, collections::BTreeMap, fmt::Debug};
2
3use inventory::{Collect, Registry};
4use thiserror::Error;
5
6/// 用于创建类型 `T` 实例的工厂 trait。
7///
8/// 实现此 trait 的类型可以创建目标类型的装箱实例。
9/// 类型 `T` 必须是 `Send + Sync` 并且可以是非固定大小类型。
10pub trait Factory<T: ?Sized> {
11    fn create(&self) -> Box<T>;
12}
13
14/// 通过工厂创建对象时可能发生的错误。
15#[derive(Debug, Error)]
16pub enum FactoryError {
17    /// 未找到指定的工厂。
18    #[error("未找到 ID 为 '{0}' 的工厂")]
19    FactoryNotFound(String),
20
21    /// 不允许回退时提供了空 ID。
22    #[error("不允许回退时提供了空 ID")]
23    EmptyIdNoFallback,
24
25    /// 请求的产品类型没有可用的工厂。
26    #[error("没有可用的工厂")]
27    NoFactoriesAvailable,
28}
29
30/// 当找不到指定 ID 的工厂时的处理策略。
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum FactoryFallback {
33    /// 如果找不到指定的工厂,则使用集合中的第一个工厂。
34    First,
35
36    /// 如果找不到指定的工厂,则使用集合中的最后一个工厂。
37    Last,
38
39    /// ID 为空时不回退,直接返回错误。
40    NoFallback,
41}
42
43/// 用于创建类型 `T` 实例的工厂集合。
44///
45/// 此结构体包含一个从工厂 ID 到工厂实例的映射,这些工厂实例可以创建
46/// 目标类型 `T` 的装箱实例。工厂在编译时使用 `inventory` crate 注册,
47/// 可以通过 `FactoryRegistry::factories()` 检索。
48///
49/// 类型 `T` 可以是非固定大小类型(trait 对象),并且必须具有 `'static` 生命周期。
50/// 工厂存储为静态引用,允许它们在线程间共享。
51pub struct SimpleFactory<T: ?Sized + 'static>(
52    BTreeMap<&'static str, &'static (dyn Factory<T> + Sync)>,
53);
54
55impl<T> SimpleFactory<T>
56where
57    T: ?Sized + 'static,
58{
59    /// 使用指定的回退策略通过工厂模式创建实例。
60    ///
61    /// 此函数通过 ID 查找工厂并使用它创建实例。
62    /// 如果 `id` 为空,行为取决于 `strategy`:
63    /// - `NoFallback`:返回错误
64    /// - `First`:使用集合中的第一个工厂
65    /// - `Last`:使用集合中的最后一个工厂
66    ///   如果 `id` 不为空但找不到工厂,行为由 `strategy` 决定。
67    ///
68    /// # 参数
69    /// * `id` - 要使用的工厂标识符,或空字符串表示默认
70    /// * `strategy` - 找不到指定 ID 的工厂时使用的策略
71    ///
72    /// # 返回值
73    /// * `Ok((&str, Box<T>))` - 成功时返回包含使用的工厂 ID 和创建的实例的元组
74    /// * `Err(FactoryError)` - 如果找不到工厂或没有可用的工厂则返回错误
75    pub fn create<'a>(
76        &self,
77        id: &'a str,
78        strategy: FactoryFallback,
79    ) -> Result<(&'a str, Box<T>), FactoryError> {
80        if !id.is_empty() {
81            return if let Some(factory) = self.0.get(id) {
82                Ok((id, factory.create()))
83            } else {
84                Err(FactoryError::FactoryNotFound(id.to_string()))
85            };
86        }
87
88        match strategy {
89            FactoryFallback::First => {
90                if let Some((id, factory)) = self.0.first_key_value() {
91                    return Ok((id, factory.create()));
92                }
93            }
94            FactoryFallback::Last => {
95                if let Some((id, factory)) = self.0.last_key_value() {
96                    return Ok((id, factory.create()));
97                }
98            }
99            FactoryFallback::NoFallback => return Err(FactoryError::EmptyIdNoFallback),
100        }
101
102        Err(FactoryError::NoFactoriesAvailable)
103    }
104}
105
106/// 工厂实现的注册表条目。
107///
108/// 存储工厂的元数据,包括其 ID、产品类型 ID 和工厂实例。
109/// 此类型与 `inventory` crate 一起用于编译时注册。
110pub struct FactoryRegistry<T>
111where
112    T: ?Sized + 'static,
113{
114    /// 此工厂的唯一标识符。
115    ///
116    /// 此 ID 用于在创建实例时查找工厂。
117    /// 它必须是静态字符串字面量,并且对于给定产品类型 `T` 在注册表中应该是唯一的。
118    id: &'static str,
119
120    /// 创建类型 `T` 实例的工厂实例。
121    ///
122    /// 这是一个静态引用,指向可以创建产品类型 `T` 的装箱实例的工厂实现。
123    /// 工厂必须是线程安全的(`Sync`)以允许在线程间共享。
124    factory: &'static (dyn Factory<T> + Sync),
125
126    /// 产品类型 `T` 的类型标识符。
127    ///
128    /// 此字段存储产品类型 `T` 的 `TypeId`,用于在从注册表检索时按产品类型过滤工厂。
129    /// 它确保只有正确产品类型的工厂包含在工厂集合中。
130    type_id: TypeId,
131}
132
133impl<T> Collect for FactoryRegistry<T>
134where
135    T: ?Sized + 'static,
136{
137    fn registry() -> &'static Registry {
138        static REGISTRY: Registry = Registry::new();
139
140        &REGISTRY
141    }
142}
143
144impl<T> FactoryRegistry<T>
145where
146    T: ?Sized + 'static,
147{
148    /// 创建一个新的工厂注册表条目。
149    ///
150    /// # 参数
151    /// * `id` - 此工厂的唯一标识符
152    /// * `factory` - 创建产品的工厂实例
153    #[inline]
154    pub const fn new(id: &'static str, factory: &'static (dyn Factory<T> + Sync)) -> Self {
155        Self {
156            id,
157            factory,
158            type_id: TypeId::of::<T>(),
159        }
160    }
161
162    /// 查找产品类型 `T` 的所有已注册工厂。
163    ///
164    /// 此函数扫描编译时工厂注册表,并返回一个 `SimpleFactory` 实例,
165    /// 该实例包含一个从工厂 ID 到工厂实例的映射,这些工厂实例生产类型 `T` 的实例。
166    /// 只包含为确切产品类型 `T` 注册的工厂。
167    ///
168    /// # 返回值
169    /// 一个 `SimpleFactory<T>` 实例,包装一个 `BTreeMap`,其中:
170    /// - 键是工厂的静态字符串标识符
171    /// - 值是实现 `Factory<T>` 的工厂实例的引用
172    pub fn simple_factory() -> SimpleFactory<T> {
173        let type_id = TypeId::of::<T>();
174        let factories = inventory::iter::<Self>()
175            .filter_map(|reg| (type_id == reg.type_id).then_some((reg.id, reg.factory)))
176            .collect();
177
178        SimpleFactory(factories)
179    }
180}
181
182/// 为产品类型注册工厂实现的宏。
183///
184/// 注册一个工厂,该工厂创建 `$implement` 的实例作为 `$product` trait 的实现。
185#[macro_export]
186macro_rules! register_factory {
187    ($product:ty, $id:literal, $implement:ty) => {
188        $crate::const_assert!(!$id.is_empty());
189        $crate::assert_impl_one!($implement: Default);
190
191        const _: () = {
192            struct ConcreteFactory;
193
194            impl $crate::Factory<$product> for ConcreteFactory {
195                fn create(&self) -> Box<$product> {
196                    Box::<$implement>::default()
197                }
198            }
199
200            $crate::submit! {
201                $crate::FactoryRegistry::new(
202                    $id,
203                    &ConcreteFactory as &'static (dyn $crate::Factory<$product> + Sync),
204                )
205            }
206        };
207    };
208}