Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions guard/src/rules/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,7 @@ pub(crate) enum FunctionName {
ToLower,
ToUpper,
UrlDecode,
Key, // <-- add this line
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Key, // <-- add this line
Key,

}

impl FunctionName {
Expand All @@ -1213,6 +1214,7 @@ impl FunctionName {
| FunctionName::ParseEpoch
| FunctionName::ParseChar => 1,
FunctionName::Now => 0,
FunctionName::Key => 1, // key expects 1 argument
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
FunctionName::Key => 1, // key expects 1 argument
FunctionName::Key => 1,

}
}
}
Expand All @@ -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)
}
Expand All @@ -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.",
))),
Expand All @@ -1282,6 +1286,7 @@ struct ParseBooleanFunction;
struct ParseCharFunction;
struct ParseEpochFunction;
struct NowFunction;
struct KeyFunction;

trait Callable {
fn call(&self, args: &[Vec<QueryResult>]) -> Result<Vec<Option<PathAwareValue>>>;
Expand All @@ -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),
}
}
}
Expand Down Expand Up @@ -2471,6 +2477,13 @@ pub(crate) fn resolve_function<'value, 'eval, 'loc: 'value>(
.collect::<Vec<_>>())
}

impl Callable for KeyFunction {
fn call(&self, args: &[Vec<QueryResult>]) -> Result<Vec<Option<PathAwareValue>>> {
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;
1 change: 1 addition & 0 deletions guard/src/rules/functions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod collections;
pub mod converters;
pub mod date_time;
pub(crate) mod key;
pub(crate) mod strings;
27 changes: 27 additions & 0 deletions guard/src/rules/functions/key.rs
Original file line number Diff line number Diff line change
@@ -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()))
}
27 changes: 27 additions & 0 deletions guard/src/rules/functions/key_tests.rs
Original file line number Diff line number Diff line change
@@ -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");
}
}