instancebuilder/
lib.rs

1use std::any::{type_name, Any, TypeId};
2use std::collections::HashMap;
3use std::fmt::Formatter;
4
5/// InstanceBuilder offers the creation of configured instances. Due to this pattern, you can for
6/// example use dependency injection in your tests without exposing those.
7///
8/// The object to be build implements the FromInstanceBuilder trait in its module.
9///
10/// ```
11/// use std::convert::Infallible;
12/// use ::instancebuilder::{BuilderError, InstanceBuilder, FromInstanceBuilder};
13///
14/// struct TestImplementation {
15///     inner: String,
16/// }
17///
18/// struct TestConfig {
19///     key: String,
20/// }
21///
22/// impl FromInstanceBuilder for TestImplementation {
23///     fn try_from_builder(builder: &InstanceBuilder) -> Result<Self, BuilderError> {
24///         let config: &TestConfig = builder.data()?;
25///         Ok(Self {
26///             inner: config.key.clone(),
27///         })
28///     }
29/// }
30///
31/// let config = TestConfig {
32///    key: String::from("help me!"),
33/// };
34///
35/// let mut  builder = InstanceBuilder::new();
36/// builder.insert(config);
37///
38/// let instance = builder.build::<TestImplementation>().unwrap();
39///
40/// ```
41pub struct InstanceBuilder {
42    data: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
43}
44
45impl InstanceBuilder {
46    pub fn new() -> Self {
47        Self {
48            data: Default::default(),
49        }
50    }
51
52    pub fn insert<D: Any + Send + Sync>(&mut self, data: D) {
53        self.data.insert(TypeId::of::<D>(), Box::new(data));
54    }
55
56    pub fn data<D: Any + Send + Sync>(&self) -> Result<&D, BuilderError> {
57        self.data_opt()
58            .ok_or_else(|| BuilderError::DataDoesNotExist {
59                ty: type_name::<D>().to_string(),
60            })
61    }
62
63    pub fn data_opt<D: Any + Send + Sync>(&self) -> Option<&D> {
64        self.data
65            .get(&TypeId::of::<D>())
66            .and_then(|d| d.downcast_ref::<D>())
67    }
68
69    pub fn build<T>(&self) -> Result<T, BuilderError>
70    where
71        T: FromInstanceBuilder,
72    {
73        T::try_from_builder(self)
74    }
75}
76
77impl Default for InstanceBuilder {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83#[derive(Debug)]
84pub enum BuilderError {
85    DataDoesNotExist { ty: String },
86    Other(String),
87}
88
89impl ::std::error::Error for BuilderError {}
90
91impl ::std::fmt::Display for BuilderError {
92    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93        match self {
94            BuilderError::DataDoesNotExist { ty } => write!(f, "data of type {ty} does not exist"),
95            BuilderError::Other(err) => {
96                write!(f, "other error: {err}")
97            }
98        }
99    }
100}
101
102pub trait FromInstanceBuilder: Sized {
103    fn try_from_builder(builder: &InstanceBuilder) -> Result<Self, BuilderError>;
104}
105
106#[cfg(test)]
107mod tests {
108    use super::{BuilderError, FromInstanceBuilder, InstanceBuilder};
109    use std::any::{Any, TypeId};
110
111    struct TestImplementation {
112        inner: String,
113    }
114
115    struct TestConfig {
116        key: String,
117    }
118
119    impl FromInstanceBuilder for TestImplementation {
120        fn try_from_builder(builder: &InstanceBuilder) -> Result<Self, BuilderError> {
121            let config: &TestConfig = builder.data()?;
122            Ok(Self {
123                inner: config.key.clone(),
124            })
125        }
126    }
127
128    #[test]
129    fn it_creates_new_instance_of_demanded_impl() {
130        let config = TestConfig {
131            key: String::from("help me!"),
132        };
133        let config_key = config.key.clone();
134        let mut builder = InstanceBuilder::new();
135        builder.insert(config);
136
137        let instance = builder.build::<TestImplementation>().unwrap();
138
139        assert_eq!(instance.type_id(), TypeId::of::<TestImplementation>());
140        assert_eq!(instance.inner, config_key);
141    }
142}