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}