use anyhow::Result;
use serde_json::Value;
use crate::assert::engine::AssertionResult;
use crate::plugins::{
ArgTypeInfo, Plugin, PluginContext, PluginPurity, PluginResult, PluginSignature, TypeInfo,
};
pub struct LenPlugin;
impl Plugin for LenPlugin {
fn name(&self) -> &str {
"len"
}
fn description(&self) -> &str {
"Returns the length of a string or array as a non-negative integer"
}
fn signature(&self) -> PluginSignature {
PluginSignature {
return_type: TypeInfo::UInt,
arg_types: &[ArgTypeInfo {
expected: TypeInfo::Any,
required: true,
default: None,
}],
purity: PluginPurity::Pure,
deterministic: true,
idempotent: true,
safe_for_rewrite: true,
arg_names: &["value"],
}
}
fn execute(&self, args: &[Value], _context: &PluginContext) -> Result<PluginResult> {
if args.len() != 1 {
return Ok(PluginResult::Assertion(AssertionResult::Error(
"len: expects exactly 1 argument".to_string(),
)));
}
let arg = &args[0];
match arg {
Value::String(s) => Ok(PluginResult::Value(Value::Number(
serde_json::Number::from(s.len()),
))),
Value::Array(arr) => Ok(PluginResult::Value(Value::Number(
serde_json::Number::from(arr.len()),
))),
_ => Ok(PluginResult::Value(Value::Null)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_context() -> PluginContext<'static> {
PluginContext::new(&Value::Null)
}
#[test]
fn test_len_plugin_name() {
let plugin = LenPlugin;
assert_eq!(plugin.name(), "len");
}
#[test]
fn test_len_plugin_string_length() {
let plugin = LenPlugin;
let context = create_context();
let result = plugin.execute(&[Value::String("hello".to_string())], &context);
assert!(result.is_ok());
if let PluginResult::Value(Value::Number(n)) = result.unwrap() {
assert_eq!(n.as_u64().unwrap(), 5);
} else {
panic!("Expected Value result with number");
}
}
#[test]
fn test_len_plugin_array_length() {
let plugin = LenPlugin;
let context = create_context();
let arr = Value::Array(vec![
Value::Number(serde_json::Number::from(1)),
Value::Number(serde_json::Number::from(2)),
Value::Number(serde_json::Number::from(3)),
]);
let result = plugin.execute(&[arr], &context);
assert!(result.is_ok());
if let PluginResult::Value(Value::Number(n)) = result.unwrap() {
assert_eq!(n.as_u64().unwrap(), 3);
} else {
panic!("Expected Value result with number");
}
}
#[test]
fn test_len_plugin_empty_string() {
let plugin = LenPlugin;
let context = create_context();
let result = plugin.execute(&[Value::String("".to_string())], &context);
assert!(result.is_ok());
if let PluginResult::Value(Value::Number(n)) = result.unwrap() {
assert_eq!(n.as_u64().unwrap(), 0);
} else {
panic!("Expected Value result with number");
}
}
#[test]
fn test_len_plugin_null_type() {
let plugin = LenPlugin;
let context = create_context();
let result = plugin.execute(&[Value::Null], &context);
assert!(result.is_ok());
if let PluginResult::Value(Value::Null) = result.unwrap() {
} else {
panic!("Expected Null result");
}
}
#[test]
fn test_len_plugin_no_args() {
let plugin = LenPlugin;
let context = create_context();
let result = plugin.execute(&[], &context);
assert!(result.is_ok());
if let PluginResult::Assertion(AssertionResult::Error(msg)) = result.unwrap() {
assert!(msg.contains("1 argument"));
} else {
panic!("Expected Error assertion result");
}
}
}