diff --git a/guard/src/rules/eval_context.rs b/guard/src/rules/eval_context.rs index 1ff1216b..1ba80b00 100644 --- a/guard/src/rules/eval_context.rs +++ b/guard/src/rules/eval_context.rs @@ -1194,6 +1194,7 @@ pub(crate) enum FunctionName { ToLower, ToUpper, UrlDecode, + Key, // <-- add this line } impl FunctionName { @@ -1213,6 +1214,7 @@ impl FunctionName { | FunctionName::ParseEpoch | FunctionName::ParseChar => 1, FunctionName::Now => 0, + FunctionName::Key => 1, // key expects 1 argument } } } @@ -1235,6 +1237,7 @@ impl std::fmt::Display for FunctionName { FunctionName::ToLower => "to_lower", FunctionName::ToUpper => "to_upper", FunctionName::UrlDecode => "url_decode", + FunctionName::Key => "key", }; write!(f, "{}", name) } @@ -1260,6 +1263,7 @@ impl TryFrom<&str> for FunctionName { "to_lower" => Ok(FunctionName::ToLower), "to_upper" => Ok(FunctionName::ToUpper), "url_decode" => Ok(FunctionName::UrlDecode), + "key" => Ok(FunctionName::Key), _ => Err(Error::ParseError(format!( "No function with the name '{name}' exists.", ))), @@ -1282,6 +1286,7 @@ struct ParseBooleanFunction; struct ParseCharFunction; struct ParseEpochFunction; struct NowFunction; +struct KeyFunction; trait Callable { fn call(&self, args: &[Vec]) -> Result>>; @@ -1305,6 +1310,7 @@ impl Callable for FunctionName { FunctionName::ParseChar => ParseCharFunction.call(args), FunctionName::ParseEpoch => ParseEpochFunction.call(args), FunctionName::Now => NowFunction.call(args), + FunctionName::Key => KeyFunction.call(args), } } } @@ -2471,6 +2477,13 @@ pub(crate) fn resolve_function<'value, 'eval, 'loc: 'value>( .collect::>()) } +impl Callable for KeyFunction { + fn call(&self, args: &[Vec]) -> Result>> { + use crate::rules::functions::key::key; + Ok(vec![Some(key(&args[0]))]) + } +} + #[cfg(test)] #[path = "eval_context_tests.rs"] pub(super) mod eval_context_tests; diff --git a/guard/src/rules/functions.rs b/guard/src/rules/functions.rs index 8dc36276..d31b788d 100644 --- a/guard/src/rules/functions.rs +++ b/guard/src/rules/functions.rs @@ -1,4 +1,5 @@ pub(crate) mod collections; pub mod converters; pub mod date_time; +pub(crate) mod key; pub(crate) mod strings; diff --git a/guard/src/rules/functions/key.rs b/guard/src/rules/functions/key.rs new file mode 100644 index 00000000..5fec3135 --- /dev/null +++ b/guard/src/rules/functions/key.rs @@ -0,0 +1,27 @@ +use crate::rules::path_value::{Path, PathAwareValue}; +use crate::rules::QueryResult; + +#[cfg(test)] +#[path = "key_tests.rs"] +mod key_tests; + +pub(crate) fn key(args: &[QueryResult]) -> PathAwareValue { + // Return the key (logical id) for the first resolved argument + for arg in args { + match arg { + QueryResult::Resolved(val) | QueryResult::Literal(val) => { + let path = val.self_path(); + // Use relative() to get the last segment/key + let key = path.relative(); + return PathAwareValue::String((path.clone(), key.to_string())); + } + QueryResult::UnResolved(unresolved) => { + let path = unresolved.traversed_to.self_path(); + let key = path.relative(); + return PathAwareValue::String((path.clone(), key.to_string())); + } + } + } + // If no key found, return empty string at root + PathAwareValue::String((Path::root(), String::new())) +} diff --git a/guard/src/rules/functions/key_tests.rs b/guard/src/rules/functions/key_tests.rs new file mode 100644 index 00000000..8acb2b66 --- /dev/null +++ b/guard/src/rules/functions/key_tests.rs @@ -0,0 +1,27 @@ +#[cfg(test)] +use crate::rules::functions::key; +use crate::rules::path_value::{Path, PathAwareValue}; +use crate::rules::QueryResult; +use std::rc::Rc; + +#[test] +fn test_key_function_returns_last_segment() { + let path = Path::new("/Resources/MyBucket".to_string(), 0, 0); + let value = PathAwareValue::String((path.clone(), "test-value".to_string())); + let result = key::key(&[QueryResult::Resolved(Rc::new(value))]); + if let PathAwareValue::String((_, key_str)) = result { + assert_eq!(key_str, "MyBucket"); + } else { + panic!("Expected String result"); + } +} + +#[test] +fn test_key_function_empty_args() { + let result = key::key(&[]); + if let PathAwareValue::String((_, key_str)) = result { + assert_eq!(key_str, ""); + } else { + panic!("Expected String result"); + } +}