env_type/environment.rs
1use crate::context::{Context, ContextMarker};
2use crate::types::{EnvError, EnvType};
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::sync::Arc;
6
7/// Environment type that holds contexts, and the current environment.
8/// The current environment is the environment type.
9/// The contexts are the context type.
10/// The context type is a key-value pair of the environment type and the value.
11/// The value is the value for the environment type.
12#[derive(Clone, Debug)]
13pub struct Environment {
14 current: EnvType,
15 contexts: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
16}
17
18/// Environment struct implementation
19/// The Environment struct has the current environment and the contexts.
20impl Environment {
21 /// Get the current environment
22 pub fn current_env(&self) -> &EnvType {
23 &self.current
24 }
25
26 /// Get the context for the context marker
27 pub fn context<M: ContextMarker>(&self) -> Option<&Context<M>> {
28 self.contexts
29 .get(&TypeId::of::<M>())
30 .and_then(|ctx| ctx.downcast_ref())
31 }
32
33 /// Get the current value for the context marker
34 pub fn current_value<M: ContextMarker>(&self) -> Option<M::Value> {
35 self.value::<M>(self.current_env())
36 }
37
38 /// Get the value for the context marker and the environment type
39 pub fn value<M: ContextMarker>(&self, env: &EnvType) -> Option<M::Value> {
40 self.context::<M>().and_then(|ctx| ctx.get_for_env(env))
41 }
42}
43
44/// Environment builder
45/// The EnvironmentBuilder is used to create an environment with the current environment and contexts.
46#[derive(Default)]
47pub struct EnvironmentBuilder {
48 current: Option<EnvType>,
49 contexts: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
50}
51
52/// EnvironmentBuilder implementation
53/// Create a new EnvironmentBuilder with the current environment and contexts.
54///
55/// # Example
56///
57/// ```
58/// use env_type::types::EnvType;
59/// use std::str::FromStr;
60/// use std::collections::HashMap;
61///
62/// use env_type::environment::EnvironmentBuilder;
63/// use env_type::context::{ContextBuilder, ContextMarker};
64///
65/// struct TestContext;
66///
67/// impl ContextMarker for TestContext {
68/// type Value = String;
69/// }
70///
71/// struct TestConfig;
72///
73/// impl From<TestConfig> for EnvType {
74/// fn from(_: TestConfig) -> Self {
75/// EnvType::Dev
76/// }
77/// }
78///
79/// let context = ContextBuilder::<TestContext>::default()
80/// .with_value(EnvType::Dev, "dev".to_string())
81/// .with_value(EnvType::Test, "test".to_string())
82/// .with_default("default".to_string())
83/// .build();
84///
85/// let env = EnvironmentBuilder::default()
86/// .current_from(TestConfig)
87/// .with_context(context)
88/// .build();
89///
90/// assert!(env.is_ok());
91/// let env = env.unwrap();
92/// assert_eq!(EnvType::Dev, *env.current_env());
93/// assert_eq!(Some("dev".to_string()), env.current_value::<TestContext>());
94/// ```
95impl EnvironmentBuilder {
96 pub fn current_env(mut self, env: EnvType) -> Self {
97 self.current = Some(env);
98 self
99 }
100
101 pub fn current_from<T>(mut self, config: T) -> Self
102 where
103 EnvType: From<T>,
104 {
105 self.current = Some(EnvType::from(config));
106 self
107 }
108
109 pub fn with_context<M: ContextMarker>(mut self, context: Context<M>) -> Self {
110 self.contexts.insert(TypeId::of::<M>(), Arc::new(context));
111 self
112 }
113
114 pub fn build(self) -> Result<Environment, EnvError> {
115 let current = self.current.ok_or(EnvError::NoCurrentEnv)?;
116
117 Ok(Environment {
118 current,
119 contexts: self.contexts,
120 })
121 }
122}