rust-patterns 0.1.0

A modern Rust design patterns library with type-safe, efficient implementations
Documentation
use std::{any::TypeId, collections::BTreeMap, fmt::Debug};

use inventory::{Collect, Registry};
use thiserror::Error;

/// 用于创建类型 `T` 实例的工厂 trait。
///
/// 实现此 trait 的类型可以创建目标类型的装箱实例。
/// 类型 `T` 必须是 `Send + Sync` 并且可以是非固定大小类型。
pub trait Factory<T: ?Sized> {
    fn create(&self) -> Box<T>;
}

/// 通过工厂创建对象时可能发生的错误。
#[derive(Debug, Error)]
pub enum FactoryError {
    /// 未找到指定的工厂。
    #[error("未找到 ID 为 '{0}' 的工厂")]
    FactoryNotFound(String),

    /// 不允许回退时提供了空 ID。
    #[error("不允许回退时提供了空 ID")]
    EmptyIdNoFallback,

    /// 请求的产品类型没有可用的工厂。
    #[error("没有可用的工厂")]
    NoFactoriesAvailable,
}

/// 当找不到指定 ID 的工厂时的处理策略。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FactoryFallback {
    /// 如果找不到指定的工厂,则使用集合中的第一个工厂。
    First,

    /// 如果找不到指定的工厂,则使用集合中的最后一个工厂。
    Last,

    /// ID 为空时不回退,直接返回错误。
    NoFallback,
}

/// 用于创建类型 `T` 实例的工厂集合。
///
/// 此结构体包含一个从工厂 ID 到工厂实例的映射,这些工厂实例可以创建
/// 目标类型 `T` 的装箱实例。工厂在编译时使用 `inventory` crate 注册,
/// 可以通过 `FactoryRegistry::factories()` 检索。
///
/// 类型 `T` 可以是非固定大小类型(trait 对象),并且必须具有 `'static` 生命周期。
/// 工厂存储为静态引用,允许它们在线程间共享。
pub struct SimpleFactory<T: ?Sized + 'static>(
    BTreeMap<&'static str, &'static (dyn Factory<T> + Sync)>,
);

impl<T> SimpleFactory<T>
where
    T: ?Sized + 'static,
{
    /// 使用指定的回退策略通过工厂模式创建实例。
    ///
    /// 此函数通过 ID 查找工厂并使用它创建实例。
    /// 如果 `id` 为空,行为取决于 `strategy`:
    /// - `NoFallback`:返回错误
    /// - `First`:使用集合中的第一个工厂
    /// - `Last`:使用集合中的最后一个工厂
    ///   如果 `id` 不为空但找不到工厂,行为由 `strategy` 决定。
    ///
    /// # 参数
    /// * `id` - 要使用的工厂标识符,或空字符串表示默认
    /// * `strategy` - 找不到指定 ID 的工厂时使用的策略
    ///
    /// # 返回值
    /// * `Ok((&str, Box<T>))` - 成功时返回包含使用的工厂 ID 和创建的实例的元组
    /// * `Err(FactoryError)` - 如果找不到工厂或没有可用的工厂则返回错误
    pub fn create<'a>(
        &self,
        id: &'a str,
        strategy: FactoryFallback,
    ) -> Result<(&'a str, Box<T>), FactoryError> {
        if !id.is_empty() {
            return if let Some(factory) = self.0.get(id) {
                Ok((id, factory.create()))
            } else {
                Err(FactoryError::FactoryNotFound(id.to_string()))
            };
        }

        match strategy {
            FactoryFallback::First => {
                if let Some((id, factory)) = self.0.first_key_value() {
                    return Ok((id, factory.create()));
                }
            }
            FactoryFallback::Last => {
                if let Some((id, factory)) = self.0.last_key_value() {
                    return Ok((id, factory.create()));
                }
            }
            FactoryFallback::NoFallback => return Err(FactoryError::EmptyIdNoFallback),
        }

        Err(FactoryError::NoFactoriesAvailable)
    }
}

/// 工厂实现的注册表条目。
///
/// 存储工厂的元数据,包括其 ID、产品类型 ID 和工厂实例。
/// 此类型与 `inventory` crate 一起用于编译时注册。
pub struct FactoryRegistry<T>
where
    T: ?Sized + 'static,
{
    /// 此工厂的唯一标识符。
    ///
    /// 此 ID 用于在创建实例时查找工厂。
    /// 它必须是静态字符串字面量,并且对于给定产品类型 `T` 在注册表中应该是唯一的。
    id: &'static str,

    /// 创建类型 `T` 实例的工厂实例。
    ///
    /// 这是一个静态引用,指向可以创建产品类型 `T` 的装箱实例的工厂实现。
    /// 工厂必须是线程安全的(`Sync`)以允许在线程间共享。
    factory: &'static (dyn Factory<T> + Sync),

    /// 产品类型 `T` 的类型标识符。
    ///
    /// 此字段存储产品类型 `T` 的 `TypeId`,用于在从注册表检索时按产品类型过滤工厂。
    /// 它确保只有正确产品类型的工厂包含在工厂集合中。
    type_id: TypeId,
}

impl<T> Collect for FactoryRegistry<T>
where
    T: ?Sized + 'static,
{
    fn registry() -> &'static Registry {
        static REGISTRY: Registry = Registry::new();

        &REGISTRY
    }
}

impl<T> FactoryRegistry<T>
where
    T: ?Sized + 'static,
{
    /// 创建一个新的工厂注册表条目。
    ///
    /// # 参数
    /// * `id` - 此工厂的唯一标识符
    /// * `factory` - 创建产品的工厂实例
    #[inline]
    pub const fn new(id: &'static str, factory: &'static (dyn Factory<T> + Sync)) -> Self {
        Self {
            id,
            factory,
            type_id: TypeId::of::<T>(),
        }
    }

    /// 查找产品类型 `T` 的所有已注册工厂。
    ///
    /// 此函数扫描编译时工厂注册表,并返回一个 `SimpleFactory` 实例,
    /// 该实例包含一个从工厂 ID 到工厂实例的映射,这些工厂实例生产类型 `T` 的实例。
    /// 只包含为确切产品类型 `T` 注册的工厂。
    ///
    /// # 返回值
    /// 一个 `SimpleFactory<T>` 实例,包装一个 `BTreeMap`,其中:
    /// - 键是工厂的静态字符串标识符
    /// - 值是实现 `Factory<T>` 的工厂实例的引用
    pub fn simple_factory() -> SimpleFactory<T> {
        let type_id = TypeId::of::<T>();
        let factories = inventory::iter::<Self>()
            .filter_map(|reg| (type_id == reg.type_id).then_some((reg.id, reg.factory)))
            .collect();

        SimpleFactory(factories)
    }
}

/// 为产品类型注册工厂实现的宏。
///
/// 注册一个工厂,该工厂创建 `$implement` 的实例作为 `$product` trait 的实现。
#[macro_export]
macro_rules! register_factory {
    ($product:ty, $id:literal, $implement:ty) => {
        $crate::const_assert!(!$id.is_empty());
        $crate::assert_impl_one!($implement: Default);

        const _: () = {
            struct ConcreteFactory;

            impl $crate::Factory<$product> for ConcreteFactory {
                fn create(&self) -> Box<$product> {
                    Box::<$implement>::default()
                }
            }

            $crate::submit! {
                $crate::FactoryRegistry::new(
                    $id,
                    &ConcreteFactory as &'static (dyn $crate::Factory<$product> + Sync),
                )
            }
        };
    };
}