telar-services-core 0.1.4

Platform path providers and an optional dependency-injection scope for Telar.
Documentation
pub mod dialogs;
pub mod paths;
#[cfg(feature = "di")]
mod registry;
#[cfg(feature = "di")]
mod scope;

pub use dialogs::{FileDialog, FileDialogs, FileFilter, file_dialogs, set_file_dialogs};
pub use paths::{AppPathsProvider, NoPaths};
#[cfg(feature = "di")]
pub use registry::ServiceError;
#[cfg(feature = "di")]
pub use scope::{Scope, ServiceContext, ServiceGuard, provide, try_inject, with_service};

#[cfg(all(test, feature = "di"))]
mod tests {
    use std::cell::RefCell;
    use std::rc::Rc;

    use super::*;
    use crate::registry::ServiceRegistry;

    #[test]
    fn registry_insert_get() {
        let mut reg = ServiceRegistry::new();
        reg.insert(42u32).unwrap();
        assert_eq!(reg.get::<u32>(), Some(&42));
    }

    #[test]
    fn registry_insert_duplicate_returns_error() {
        let mut reg = ServiceRegistry::new();
        assert!(reg.insert(1u32).is_ok());
        assert_eq!(reg.insert(2u32), Err(ServiceError::AlreadyRegistered));
        assert_eq!(reg.get::<u32>(), Some(&1));
    }

    #[test]
    fn registry_missing_type() {
        let reg = ServiceRegistry::new();
        assert_eq!(reg.get::<u32>(), None);
    }

    #[test]
    fn scope_provide_inject() {
        Scope::with(|| {
            provide(99u32).unwrap();
            assert_eq!(try_inject::<u32>(), Some(99));
        });
    }

    #[test]
    fn scope_child_inherits_parent() {
        Scope::with(|| {
            provide(String::from("from-parent")).unwrap();
            Scope::with(|| {
                assert_eq!(try_inject::<String>(), Some(String::from("from-parent")));
            });
        });
    }

    #[test]
    fn scope_child_shadows_parent() {
        Scope::with(|| {
            provide(1u32).unwrap();
            Scope::with(|| {
                provide(2u32).unwrap();
                assert_eq!(try_inject::<u32>(), Some(2));
            });
        });
    }

    #[test]
    fn scope_drop_restores_parent() {
        Scope::with(|| {
            provide(String::from("parent")).unwrap();
            Scope::with(|| {
                provide(String::from("child")).unwrap();
                assert_eq!(try_inject::<String>().as_deref(), Some("child"));
            });
            assert_eq!(try_inject::<String>().as_deref(), Some("parent"));
        });
    }

    #[test]
    fn with_service_non_clone() {
        Scope::with(|| {
            provide(Rc::new(RefCell::new(vec![1, 2, 3]))).unwrap();
            let len = with_service(|v: &Rc<RefCell<Vec<i32>>>| v.borrow().len());
            assert_eq!(len, Some(3));
        });
    }

    #[test]
    fn try_inject_missing_returns_none() {
        Scope::with(|| {
            assert_eq!(try_inject::<u64>(), None);
        });
    }

    #[test]
    fn scope_with_provides_service() {
        Scope::with(|| {
            provide(42u32).unwrap();
            assert_eq!(try_inject::<u32>(), Some(42));
        });
        assert_eq!(try_inject::<u32>(), None);
    }

    #[test]
    fn scope_with_cleans_up_after_closure_returns() {
        Scope::with(|| {
            provide(String::from("ephemeral")).unwrap();
            assert!(try_inject::<String>().is_some());
        });
        assert_eq!(
            try_inject::<String>(),
            None,
            "service must not be visible after Scope::with returns"
        );
    }

    #[test]
    fn scope_with_nested() {
        Scope::with(|| {
            provide(String::from("outer")).unwrap();
            Scope::with(|| {
                provide(String::from("inner")).unwrap();
                assert_eq!(try_inject::<String>().as_deref(), Some("inner"));
            });
            assert_eq!(try_inject::<String>().as_deref(), Some("outer"));
        });
    }
}