1+ use std:: borrow:: Cow ;
2+
13use async_trait:: async_trait;
24use open_feature:: {
35 provider:: { FeatureProvider , ProviderMetadata , ResolutionDetails } ,
@@ -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
@@ -71,7 +81,7 @@ impl FeatureProvider for EnvVarProvider {
7181 flag_key : & str ,
7282 evaluation_context : & EvaluationContext ,
7383 ) -> EvaluationResult < ResolutionDetails < bool > > {
74- return evaluate_environment_variable ( flag_key, evaluation_context) ;
84+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
7585 }
7686
7787 /// The 64-bit signed integer type.
@@ -93,7 +103,7 @@ impl FeatureProvider for EnvVarProvider {
93103 flag_key : & str ,
94104 evaluation_context : & EvaluationContext ,
95105 ) -> EvaluationResult < ResolutionDetails < i64 > > {
96- return evaluate_environment_variable ( flag_key, evaluation_context) ;
106+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
97107 }
98108
99109 /// A 64-bit floating point type
@@ -120,7 +130,7 @@ impl FeatureProvider for EnvVarProvider {
120130 flag_key : & str ,
121131 evaluation_context : & EvaluationContext ,
122132 ) -> EvaluationResult < ResolutionDetails < f64 > > {
123- return evaluate_environment_variable ( flag_key, evaluation_context) ;
133+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
124134 }
125135
126136 /// A UTF-8 encoded string.
@@ -145,7 +155,7 @@ impl FeatureProvider for EnvVarProvider {
145155 flag_key : & str ,
146156 evaluation_context : & EvaluationContext ,
147157 ) -> EvaluationResult < ResolutionDetails < String > > {
148- return evaluate_environment_variable ( flag_key, evaluation_context) ;
158+ return evaluate_environment_variable ( & self . rename , flag_key, evaluation_context) ;
149159 }
150160
151161 /// Structured data, presented however is idiomatic in the implementation language, such as JSON or YAML.
@@ -172,11 +182,13 @@ impl FeatureProvider for EnvVarProvider {
172182/// assert_eq!(res.unwrap_err().code, EvaluationErrorCode::FlagNotFound);
173183/// }
174184/// ```
175- fn evaluate_environment_variable < T : std:: str:: FromStr > (
185+ fn evaluate_environment_variable < R : Rename , T : std:: str:: FromStr > (
186+ rename : & R ,
176187 flag_key : & str ,
177188 _evaluation_context : & EvaluationContext ,
178189) -> EvaluationResult < ResolutionDetails < T > > {
179- match std:: env:: var ( flag_key) {
190+ let env_var = rename. rename ( flag_key) ;
191+ match std:: env:: var ( env_var. as_ref ( ) ) {
180192 Ok ( value) => match value. parse :: < T > ( ) {
181193 Ok ( parsed_value) => EvaluationResult :: Ok (
182194 ResolutionDetails :: builder ( )
@@ -208,6 +220,51 @@ fn error<T>(evaluation_error_code: EvaluationErrorCode) -> EvaluationResult<T> {
208220 . build ( ) )
209221}
210222
223+ /// Rename helps converting flag keys to environment variable names
224+ ///
225+ /// # Example
226+ /// ```rust
227+ /// fn underscore(flag_key: &str) -> std::borrow::Cow<'_, str> {
228+ /// flag_key.replace("-", "_").to_uppercase().into()
229+ /// }
230+ ///
231+ /// #[tokio::test]
232+ /// async fn test_rename() {
233+ /// let flag_key = "test-rename-key";
234+ /// let flag_value = std::f64::consts::PI.to_string();
235+ /// let provider = EnvVarProvider::new(underscore);
236+ ///
237+ /// std::env::set_var("TEST_RENAME_KEY", &flag_value);
238+ ///
239+ /// let result = provider
240+ /// .resolve_float_value(flag_key, &EvaluationContext::default())
241+ /// .await;
242+ /// assert!(result.is_ok());
243+ /// assert_eq!(result.unwrap().value, flag_value.parse::<f64>().unwrap());
244+ /// }
245+ /// ```
246+ pub trait Rename : Send + Sync + ' static {
247+ fn rename < ' a > ( & self , flag_key : & ' a str ) -> Cow < ' a , str > ;
248+ }
249+
250+ #[ derive( Copy , Clone , Default , Debug ) ]
251+ pub struct NoopRename ;
252+
253+ impl Rename for NoopRename {
254+ fn rename < ' a > ( & self , flag_key : & ' a str ) -> Cow < ' a , str > {
255+ flag_key. into ( )
256+ }
257+ }
258+
259+ impl < F > Rename for F
260+ where
261+ F : Fn ( & str ) -> Cow < ' _ , str > + Send + Sync + ' static ,
262+ {
263+ fn rename < ' a > ( & self , flag_key : & ' a str ) -> Cow < ' a , str > {
264+ ( self ) ( flag_key)
265+ }
266+ }
267+
211268#[ cfg( test) ]
212269mod tests {
213270
@@ -230,4 +287,30 @@ mod tests {
230287 assert ! ( provider. resolve_string_value( "" , & context) . await . is_err( ) ) ;
231288 assert ! ( provider. resolve_struct_value( "" , & context) . await . is_err( ) ) ;
232289 }
290+
291+ #[ test]
292+ fn noop_rename_does_nothing ( ) {
293+ let flag_key = "test-key" ;
294+ assert_eq ! ( NoopRename . rename( flag_key) , flag_key) ;
295+ }
296+
297+ fn underscore ( flag_key : & str ) -> Cow < ' _ , str > {
298+ flag_key. replace ( "-" , "_" ) . to_uppercase ( ) . into ( )
299+ }
300+
301+ #[ tokio:: test]
302+ async fn resolves_with_a_custom_rename ( ) {
303+ let provider = EnvVarProvider :: new ( underscore) ;
304+ let context = EvaluationContext :: default ( ) ;
305+
306+ std:: env:: set_var ( "HELLO_WORLD" , "true" ) ;
307+
308+ assert ! (
309+ provider
310+ . resolve_bool_value( "hello-world" , & context)
311+ . await
312+ . unwrap( )
313+ . value
314+ ) ;
315+ }
233316}
0 commit comments