use anyhow::Result;
use serde_json::Value;
use std::env;
use crate::assert::engine::AssertionResult;
use crate::plugins::{
ArgTypeInfo, Plugin, PluginContext, PluginPurity, PluginResult, PluginSignature, TypeInfo,
};
#[derive(Debug, Clone, Default)]
pub struct EnvPlugin;
impl Plugin for EnvPlugin {
fn name(&self) -> &'static str {
"env"
}
fn description(&self) -> &'static str {
"Read environment variable value. Optional second argument provides a default value."
}
fn signature(&self) -> PluginSignature {
PluginSignature {
return_type: TypeInfo::StringOrNull,
arg_types: &[
ArgTypeInfo {
expected: TypeInfo::String,
required: true,
default: None,
},
ArgTypeInfo {
expected: TypeInfo::String,
required: false,
default: None,
},
],
purity: PluginPurity::Impure,
deterministic: false,
idempotent: false,
safe_for_rewrite: false,
arg_names: &["name", "default"],
}
}
fn execute(&self, args: &[Value], _context: &PluginContext) -> Result<PluginResult> {
if args.is_empty() {
return Ok(PluginResult::Assertion(AssertionResult::fail(
"@env requires at least 1 argument: the variable name",
)));
}
if args.len() > 2 {
return Ok(PluginResult::Assertion(AssertionResult::fail(
"@env accepts at most 2 arguments: name and optional default value",
)));
}
let var_name = match &args[0] {
Value::String(name) => name,
_ => {
return Ok(PluginResult::Assertion(AssertionResult::fail(
"@env first argument must be a string (variable name)",
)));
}
};
let default_value = if args.len() > 1 {
match &args[1] {
Value::String(s) => Some(s.clone()),
other => Some(other.to_string()),
}
} else {
None
};
match env::var(var_name) {
Ok(value) => Ok(PluginResult::Value(Value::String(value))),
Err(_) => {
if let Some(default) = default_value {
Ok(PluginResult::Value(Value::String(default)))
} else {
Ok(PluginResult::Value(Value::Null))
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_env_plugin_exists() {
let plugin = EnvPlugin;
unsafe {
env::set_var("TEST_VAR", "test_value");
}
let result = plugin
.execute(
&[Value::String("TEST_VAR".to_string())],
&PluginContext::new(&Value::Null),
)
.unwrap();
assert!(matches!(result, PluginResult::Value(Value::String(s)) if s == "test_value"));
unsafe {
env::remove_var("TEST_VAR");
}
}
#[test]
fn test_env_plugin_not_exists() {
let plugin = EnvPlugin;
let result = plugin
.execute(
&[Value::String("NONEXISTENT_VAR_12345".to_string())],
&PluginContext::new(&Value::Null),
)
.unwrap();
assert!(matches!(result, PluginResult::Value(Value::Null)));
}
#[test]
fn test_env_plugin_no_args() {
let plugin = EnvPlugin;
let result = plugin
.execute(&[], &PluginContext::new(&Value::Null))
.unwrap();
assert!(matches!(
result,
PluginResult::Assertion(AssertionResult::Fail { .. })
));
}
#[test]
fn test_env_plugin_too_many_args() {
let plugin = EnvPlugin;
let result = plugin
.execute(
&[
Value::String("VAR1".to_string()),
Value::String("default".to_string()),
Value::String("extra".to_string()),
],
&PluginContext::new(&Value::Null),
)
.unwrap();
assert!(matches!(
result,
PluginResult::Assertion(AssertionResult::Fail { .. })
));
}
#[test]
fn test_env_plugin_with_default_value() {
let plugin = EnvPlugin;
let result = plugin
.execute(
&[
Value::String("NONEXISTENT_VAR_12345".to_string()),
Value::String("/home/user".to_string()),
],
&PluginContext::new(&Value::Null),
)
.unwrap();
assert!(matches!(result, PluginResult::Value(Value::String(s)) if s == "/home/user"));
}
#[test]
fn test_env_plugin_var_exists_ignores_default() {
let plugin = EnvPlugin;
unsafe {
env::set_var("TEST_VAR_DEFAULT", "actual_value");
}
let result = plugin
.execute(
&[
Value::String("TEST_VAR_DEFAULT".to_string()),
Value::String("ignored_default".to_string()),
],
&PluginContext::new(&Value::Null),
)
.unwrap();
assert!(matches!(result, PluginResult::Value(Value::String(s)) if s == "actual_value"));
unsafe {
env::remove_var("TEST_VAR_DEFAULT");
}
}
#[test]
fn test_env_plugin_wrong_type() {
let plugin = EnvPlugin;
let result = plugin
.execute(
&[Value::Number(123.into())],
&PluginContext::new(&Value::Null),
)
.unwrap();
assert!(matches!(
result,
PluginResult::Assertion(AssertionResult::Fail { .. })
));
}
#[test]
fn test_env_plugin_name() {
let plugin = EnvPlugin;
assert_eq!(plugin.name(), "env");
}
#[test]
fn test_env_plugin_description() {
let plugin = EnvPlugin;
assert!(plugin.description().contains("environment"));
}
}