env_type/
context.rs

1use crate::types::{EnvError, EnvType};
2use std::collections::HashMap;
3use std::marker::PhantomData;
4
5/// Context marker trait for type-safe context values
6/// The Value type must be Clone, Send, Sync, and 'static strictly.
7pub trait ContextMarker: Send + Sync + 'static {
8    type Value: Clone + Send + Sync + 'static;
9}
10
11/// Context is Generic context container
12/// The context is a key-value store for environment values.
13#[derive(Clone)]
14pub struct Context<M: ContextMarker> {
15    /// Environment values and values for each environment
16    env_values: HashMap<EnvType, M::Value>,
17    /// Default value for the context, if no value is found for the environment
18    default: Option<M::Value>,
19    /// Marker for the context type
20    _marker: PhantomData<M>,
21}
22
23/// Context implementation for ContextMarker
24///
25/// # Example
26///
27/// ```
28/// use env_type::context::{ContextMarker, Context};
29/// use env_type::types::EnvType;
30///
31/// struct TestContext;
32///
33/// impl ContextMarker for TestContext {
34///   type Value = String;
35/// }
36///
37/// let mut context = Context::<TestContext>::default();
38/// assert!(context.get_for_env(&EnvType::Dev).is_none());
39/// ```
40
41impl<M: ContextMarker> Default for Context<M> {
42    fn default() -> Self {
43        Self {
44            env_values: HashMap::new(),
45            default: None,
46            _marker: PhantomData,
47        }
48    }
49}
50
51impl<M: ContextMarker> Context<M> {
52    /// Get the value for the current environment
53    /// If no value is found, return the default value(optional)
54    pub fn get_for_env(&self, env: &EnvType) -> Option<M::Value> {
55        self.env_values
56            .get(env)
57            .cloned()
58            .or_else(|| self.default.clone())
59    }
60
61    /// Try to get the value for the current environment
62    /// If no value is found, return an error
63    pub fn try_get_for_env(&self, env: &EnvType) -> Result<M::Value, EnvError> {
64        self.get_for_env(env).ok_or(EnvError::ContextValueNotFound)
65    }
66}
67
68/// Builder for type-safe context configuration
69/// The builder is used to create a context with environment values and a default value.
70///
71/// # Example
72///
73/// ```
74/// use env_type::context::{ContextBuilder, ContextMarker};
75/// use env_type::types::{EnvType};
76///
77/// struct TestContext;
78///
79/// impl ContextMarker for TestContext {
80///  type Value = String;
81/// }
82///
83/// let context = ContextBuilder::<TestContext>::default()
84///  .with_value(EnvType::Dev, "dev".to_string())
85///  .with_value(EnvType::Test, "test".to_string())
86///  .with_default("default".to_string())
87///  .build();
88///
89/// assert_eq!(context.get_for_env(&EnvType::Dev), Some("dev".to_string()));
90/// assert_eq!(context.get_for_env(&EnvType::Test), Some("test".to_string()));
91/// assert_eq!(context.get_for_env(&EnvType::Stg), Some("default".to_string()));
92/// ```
93pub struct ContextBuilder<M: ContextMarker> {
94    env_values: HashMap<EnvType, M::Value>,
95    default: Option<M::Value>,
96    _marker: PhantomData<M>,
97}
98
99impl<M: ContextMarker> Default for ContextBuilder<M> {
100    fn default() -> Self {
101        Self {
102            env_values: HashMap::new(),
103            default: None,
104            _marker: PhantomData,
105        }
106    }
107}
108
109/// ContextBuilder implementation
110/// Create a new ContextBuilder with the environment values and default value.
111impl<M: ContextMarker> ContextBuilder<M> {
112    pub fn with_value(mut self, env: EnvType, value: M::Value) -> Self {
113        self.env_values.insert(env, value);
114        self
115    }
116
117    pub fn with_values<I>(mut self, envs: I, value: M::Value) -> Self
118    where
119        I: IntoIterator<Item = EnvType>,
120        M::Value: Clone,
121    {
122        for env in envs {
123            self.env_values.insert(env, value.clone());
124        }
125        self
126    }
127
128    pub fn with_default(mut self, value: M::Value) -> Self {
129        self.default = Some(value);
130        self
131    }
132
133    pub fn build(self) -> Context<M> {
134        Context {
135            env_values: self.env_values,
136            default: self.default,
137            _marker: PhantomData,
138        }
139    }
140}