use async_trait::async_trait;
use open_feature::{
provider::{FeatureProvider, ProviderMetadata, ResolutionDetails},
EvaluationContext, EvaluationError, EvaluationErrorCode, EvaluationReason, EvaluationResult,
StructValue,
};
const METADATA: &str = "Environment Variables Provider";
#[derive(Debug)]
pub struct EnvVarProvider {
metadata: ProviderMetadata,
}
impl Default for EnvVarProvider {
fn default() -> Self {
Self {
metadata: ProviderMetadata::new(METADATA),
}
}
}
#[async_trait]
impl FeatureProvider for EnvVarProvider {
fn metadata(&self) -> &ProviderMetadata {
&self.metadata
}
async fn resolve_bool_value(
&self,
flag_key: &str,
evaluation_context: &EvaluationContext,
) -> EvaluationResult<ResolutionDetails<bool>> {
return evaluate_environment_variable(flag_key, evaluation_context);
}
async fn resolve_int_value(
&self,
flag_key: &str,
evaluation_context: &EvaluationContext,
) -> EvaluationResult<ResolutionDetails<i64>> {
return evaluate_environment_variable(flag_key, evaluation_context);
}
async fn resolve_float_value(
&self,
flag_key: &str,
evaluation_context: &EvaluationContext,
) -> EvaluationResult<ResolutionDetails<f64>> {
return evaluate_environment_variable(flag_key, evaluation_context);
}
async fn resolve_string_value(
&self,
flag_key: &str,
evaluation_context: &EvaluationContext,
) -> EvaluationResult<ResolutionDetails<String>> {
return evaluate_environment_variable(flag_key, evaluation_context);
}
async fn resolve_struct_value(
&self,
_flag_key: &str,
_evaluation_context: &EvaluationContext,
) -> EvaluationResult<ResolutionDetails<StructValue>> {
return error(EvaluationErrorCode::General(
"Structs are not supported".to_string(),
));
}
}
fn evaluate_environment_variable<T: std::str::FromStr>(
flag_key: &str,
_evaluation_context: &EvaluationContext,
) -> EvaluationResult<ResolutionDetails<T>> {
match std::env::var(flag_key) {
Ok(value) => match value.parse::<T>() {
Ok(parsed_value) => EvaluationResult::Ok(
ResolutionDetails::builder()
.value(parsed_value)
.reason(EvaluationReason::Static)
.build(),
),
Err(_) => error(EvaluationErrorCode::TypeMismatch),
},
Err(_) => error(EvaluationErrorCode::FlagNotFound),
}
}
fn error<T>(evaluation_error_code: EvaluationErrorCode) -> EvaluationResult<T> {
Err(EvaluationError::builder()
.message("Error evaluating environment variable")
.code(evaluation_error_code)
.build())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metadata() {
let provider = EnvVarProvider::default();
assert_eq!(provider.metadata().name, "Environment Variables Provider");
}
#[tokio::test]
async fn resolve_err_values() {
let provider = EnvVarProvider::default();
let context = EvaluationContext::default();
assert!(provider.resolve_bool_value("", &context).await.is_err());
assert!(provider.resolve_int_value("", &context).await.is_err());
assert!(provider.resolve_float_value("", &context).await.is_err());
assert!(provider.resolve_string_value("", &context).await.is_err());
assert!(provider.resolve_struct_value("", &context).await.is_err());
}
}