flow-di 0.1.0

A dependency injection framework for Rust inspired by C# AutoFac and Microsoft.Extensions.DependencyInjection
Documentation
use std::any::TypeId;
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};

/// Service key for identifying and distinguishing different services
/// Supports both type-based keys and named keys
#[derive(Clone)]
pub enum ServiceKey {
    /// Type-based key - uses TypeId as unique identifier
    Type(TypeId),

    /// Name-based key - combines TypeId and name as unique identifier
    Named { type_id: TypeId, name: String },
}

impl ServiceKey {
    /// Create a type-based service key
    pub fn of_type<T: 'static>() -> Self {
        ServiceKey::Type(TypeId::of::<T>())
    }

    /// Create a name-based service key
    pub fn named<T: 'static>(name: impl Into<String>) -> Self {
        ServiceKey::Named {
            type_id: TypeId::of::<T>(),
            name: name.into(),
        }
    }

    /// Get type ID
    pub fn type_id(&self) -> TypeId {
        match self {
            ServiceKey::Type(type_id) => *type_id,
            ServiceKey::Named { type_id, .. } => *type_id,
        }
    }

    /// Get name (if any)
    pub fn name(&self) -> Option<&str> {
        match self {
            ServiceKey::Type(_) => None,
            ServiceKey::Named { name, .. } => Some(name),
        }
    }

    /// Check if it's a type-based key
    pub fn is_named(&self) -> bool {
        matches!(self, ServiceKey::Named { .. })
    }

    /// Get type name
    pub fn type_name(&self) -> &'static str {
        match self {
            ServiceKey::Type(_type_id) => {
                // Cannot get type name directly from TypeId here, but can use other methods
                "Unknown"
            }
            ServiceKey::Named { .. } => "Unknown",
        }
    }
}

impl PartialEq for ServiceKey {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (ServiceKey::Type(a), ServiceKey::Type(b)) => a == b,
            (
                ServiceKey::Named {
                    type_id: a_type,
                    name: a_name,
                },
                ServiceKey::Named {
                    type_id: b_type,
                    name: b_name,
                },
            ) => a_type == b_type && a_name == b_name,
            _ => false,
        }
    }
}

impl Eq for ServiceKey {}

impl Hash for ServiceKey {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            ServiceKey::Type(type_id) => {
                0u8.hash(state); // Distinguish enum variant
                type_id.hash(state);
            }
            ServiceKey::Named { type_id, name } => {
                1u8.hash(state); // Distinguish enum variant
                type_id.hash(state);
                name.hash(state);
            }
        }
    }
}

impl Debug for ServiceKey {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            ServiceKey::Type(type_id) => {
                write!(f, "ServiceKey::Type({type_id:?})")
            }
            ServiceKey::Named { type_id, name } => {
                write!(f, "ServiceKey::Named(type: {type_id:?}, name: '{name}')")
            }
        }
    }
}

impl Display for ServiceKey {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            ServiceKey::Type(_) => {
                write!(f, "Type Service")
            }
            ServiceKey::Named { name, .. } => {
                write!(f, "Named Service '{name}'")
            }
        }
    }
}

/// Convenient macro for creating service keys
#[macro_export]
macro_rules! service_key {
    ($type:ty) => {
        $crate::ServiceKey::of_type::<$type>()
    };
    ($type:ty, $name:expr) => {
        $crate::ServiceKey::named::<$type>($name)
    };
}

#[cfg(test)]
mod tests {
    use super::*;

    struct TestService;
    struct AnotherService;

    #[test]
    fn test_type_key_creation() {
        let key1 = ServiceKey::of_type::<TestService>();
        let key2 = ServiceKey::of_type::<TestService>();
        let key3 = ServiceKey::of_type::<AnotherService>();

        assert_eq!(key1, key2);
        assert_ne!(key1, key3);
        assert!(!key1.is_named());
        assert!(key1.name().is_none());
    }

    #[test]
    fn test_named_key_creation() {
        let key1 = ServiceKey::named::<TestService>("primary");
        let key2 = ServiceKey::named::<TestService>("primary");
        let key3 = ServiceKey::named::<TestService>("secondary");
        let key4 = ServiceKey::named::<AnotherService>("primary");

        assert_eq!(key1, key2);
        assert_ne!(key1, key3);
        assert_ne!(key1, key4);
        assert!(key1.is_named());
        assert_eq!(key1.name(), Some("primary"));
    }

    #[test]
    fn test_type_vs_named_key() {
        let type_key = ServiceKey::of_type::<TestService>();
        let named_key = ServiceKey::named::<TestService>("test");

        assert_ne!(type_key, named_key);
        assert_eq!(type_key.type_id(), named_key.type_id());
    }

    #[test]
    fn test_macro() {
        let key1 = service_key!(TestService);
        let key2 = service_key!(TestService, "named");

        assert_eq!(key1, ServiceKey::of_type::<TestService>());
        assert_eq!(key2, ServiceKey::named::<TestService>("named"));
    }
}