1use std::any::{type_name, Any, TypeId};
2use std::collections::HashMap;
3use std::fmt::Formatter;
4
5pub 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}