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}