1+ use std:: borrow:: Cow ;
2+
13use async_trait:: async_trait;
24use open_feature:: {
35 EvaluationContext , EvaluationError , EvaluationErrorCode , EvaluationReason , EvaluationResult ,
@@ -19,22 +21,30 @@ const METADATA: &str = "Environment Variables Provider";
1921///
2022/// The provider will return [`EvaluationResult::Err(EvaluationError)`] if the flag is not found or if the value is not of the expected type.
2123#[ derive( Debug ) ]
22- pub struct EnvVarProvider {
24+ pub struct EnvVarProvider < R = NoopRename > {
2325 metadata : ProviderMetadata ,
26+ rename : R ,
2427}
2528
2629/// Default implementation for the Environment Variables Provider
2730impl Default for EnvVarProvider {
2831 fn default ( ) -> Self {
32+ Self :: new ( NoopRename )
33+ }
34+ }
35+
36+ impl < R > EnvVarProvider < R > {
37+ pub fn new ( rename : R ) -> Self {
2938 Self {
3039 metadata : ProviderMetadata :: new ( METADATA ) ,
40+ rename,
3141 }
3242 }
3343}
3444
3545/// Implementation of the FeatureProvider trait for the Environment Variables Provider
3646#[ async_trait]
37- impl FeatureProvider for EnvVarProvider {
47+ impl < R : Rename > FeatureProvider for EnvVarProvider < R > {
3848 /// Returns the provider metadata
3949 /// # Example
4050 /// ```rust
@@ -73,7 +83,7 @@ impl FeatureProvider for EnvVarProvider {
7383 flag_key : & str ,
7484 evaluation_context : & EvaluationContext ,
7585 ) -> EvaluationResult < ResolutionDetails < bool > > {
76- return evaluate_environment_variable ( flag_key, evaluation_context) ;
86+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
7787 }
7888
7989 /// The 64-bit signed integer type.
@@ -97,7 +107,7 @@ impl FeatureProvider for EnvVarProvider {
97107 flag_key : & str ,
98108 evaluation_context : & EvaluationContext ,
99109 ) -> EvaluationResult < ResolutionDetails < i64 > > {
100- return evaluate_environment_variable ( flag_key, evaluation_context) ;
110+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
101111 }
102112
103113 /// A 64-bit floating point type
@@ -125,7 +135,7 @@ impl FeatureProvider for EnvVarProvider {
125135 flag_key : & str ,
126136 evaluation_context : & EvaluationContext ,
127137 ) -> EvaluationResult < ResolutionDetails < f64 > > {
128- return evaluate_environment_variable ( flag_key, evaluation_context) ;
138+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
129139 }
130140
131141 /// A UTF-8 encoded string.
@@ -152,7 +162,7 @@ impl FeatureProvider for EnvVarProvider {
152162 flag_key : & str ,
153163 evaluation_context : & EvaluationContext ,
154164 ) -> EvaluationResult < ResolutionDetails < String > > {
155- return evaluate_environment_variable ( flag_key, evaluation_context) ;
165+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
156166 }
157167
158168 /// Structured data, presented however is idiomatic in the implementation language, such as JSON or YAML.
@@ -179,11 +189,13 @@ impl FeatureProvider for EnvVarProvider {
179189/// assert_eq!(res.unwrap_err().code, EvaluationErrorCode::FlagNotFound);
180190/// }
181191/// ```
182- fn evaluate_environment_variable < T : std:: str:: FromStr > (
192+ fn evaluate_environment_variable < R : Rename , T : std:: str:: FromStr > (
193+ rename : & R ,
183194 flag_key : & str ,
184195 _evaluation_context : & EvaluationContext ,
185196) -> EvaluationResult < ResolutionDetails < T > > {
186- match std:: env:: var ( flag_key) {
197+ let env_var = rename. rename ( flag_key) ;
198+ match std:: env:: var ( env_var. as_ref ( ) ) {
187199 Ok ( value) => match value. parse :: < T > ( ) {
188200 Ok ( parsed_value) => EvaluationResult :: Ok (
189201 ResolutionDetails :: builder ( )
@@ -215,6 +227,51 @@ fn error<T>(evaluation_error_code: EvaluationErrorCode) -> EvaluationResult<T> {
215227 . build ( ) )
216228}
217229
230+ /// Rename helps converting flag keys to environment variable names
231+ ///
232+ /// # Example
233+ /// ```rust
234+ /// fn underscore(flag_key: &str) -> std::borrow::Cow<'_, str> {
235+ /// flag_key.replace("-", "_").to_uppercase().into()
236+ /// }
237+ ///
238+ /// #[tokio::test]
239+ /// async fn test_rename() {
240+ /// let flag_key = "test-rename-key";
241+ /// let flag_value = std::f64::consts::PI.to_string();
242+ /// let provider = EnvVarProvider::new(underscore);
243+ ///
244+ /// std::env::set_var("TEST_RENAME_KEY", &flag_value);
245+ ///
246+ /// let result = provider
247+ /// .resolve_float_value(flag_key, &EvaluationContext::default())
248+ /// .await;
249+ /// assert!(result.is_ok());
250+ /// assert_eq!(result.unwrap().value, flag_value.parse::<f64>().unwrap());
251+ /// }
252+ /// ```
253+ pub trait Rename : Send + Sync + ' static {
254+ fn rename < ' a > ( & self , flag_key : & ' a str ) -> Cow < ' a , str > ;
255+ }
256+
257+ #[ derive( Copy , Clone , Default , Debug ) ]
258+ pub struct NoopRename ;
259+
260+ impl Rename for NoopRename {
261+ fn rename < ' a > ( & self , flag_key : & ' a str ) -> Cow < ' a , str > {
262+ flag_key. into ( )
263+ }
264+ }
265+
266+ impl < F > Rename for F
267+ where
268+ F : Fn ( & str ) -> Cow < ' _ , str > + Send + Sync + ' static ,
269+ {
270+ fn rename < ' a > ( & self , flag_key : & ' a str ) -> Cow < ' a , str > {
271+ ( self ) ( flag_key)
272+ }
273+ }
274+
218275#[ cfg( test) ]
219276mod tests {
220277
@@ -237,4 +294,32 @@ mod tests {
237294 assert ! ( provider. resolve_string_value( "" , & context) . await . is_err( ) ) ;
238295 assert ! ( provider. resolve_struct_value( "" , & context) . await . is_err( ) ) ;
239296 }
297+
298+ #[ test]
299+ fn noop_rename_does_nothing ( ) {
300+ let flag_key = "test-key" ;
301+ assert_eq ! ( NoopRename . rename( flag_key) , flag_key) ;
302+ }
303+
304+ fn underscore ( flag_key : & str ) -> Cow < ' _ , str > {
305+ flag_key. replace ( "-" , "_" ) . to_uppercase ( ) . into ( )
306+ }
307+
308+ #[ tokio:: test]
309+ async fn resolves_with_a_custom_rename ( ) {
310+ let provider = EnvVarProvider :: new ( underscore) ;
311+ let context = EvaluationContext :: default ( ) ;
312+
313+ unsafe {
314+ std:: env:: set_var ( "HELLO_WORLD" , "true" ) ;
315+ }
316+
317+ assert ! (
318+ provider
319+ . resolve_bool_value( "hello-world" , & context)
320+ . await
321+ . unwrap( )
322+ . value
323+ ) ;
324+ }
240325}
0 commit comments