// functions.mbt — Matcher function registry and built-in functions.
//
// Casbin resolves function calls in matchers through a per-enforcer
// function map. This module provides the registry plus the built-ins that
// need no external dependency: `keyMatch` and `keyGet`. The regex-,
// glob-, and IP-based built-ins (`regexMatch`, `keyMatch2`..`keyMatch5`,
// `globMatch`, `ipMatch`) arrive with their own modules.
//
// Arguments are validated strictly: a wrong argument count or a
// non-string argument raises `MatcherEval`, matching the behavior of
// Casbin's built-in operator wrappers.
///|
/// A registry of matcher functions keyed by name.
pub(all) struct FunctionRegistry {
entries : Array[(String, (Array[Value]) -> Value raise CasbinError)]
}
///|
/// Creates an empty registry.
pub fn FunctionRegistry::new() -> FunctionRegistry {
{ entries: [], }
}
///|
/// Registers `function` under `name`, replacing an existing entry.
pub fn FunctionRegistry::add(
self : FunctionRegistry,
name : String,
function : (Array[Value]) -> Value raise CasbinError,
) -> Unit {
for i in 0.. ((Array[Value]) -> Value raise CasbinError)? {
for entry in self.entries {
if entry.0 == name {
return Some(entry.1)
}
}
None
}
///|
/// A registry containing the built-in functions.
pub fn builtin_functions() -> FunctionRegistry {
let registry = FunctionRegistry::new()
registry.add("keyMatch", key_match_function)
registry.add("keyMatch2", key_match2_function)
registry.add("keyMatch3", key_match3_function)
registry.add("keyMatch4", key_match4_function)
registry.add("keyMatch5", key_match5_function)
registry.add("keyGet", key_get_function)
registry.add("keyGet2", key_get2_function)
registry.add("keyGet3", key_get3_function)
registry.add("regexMatch", regex_match_function)
registry.add("globMatch", glob_match_function)
registry.add("ipMatch", ip_match_function)
registry
}
///|
fn key_match_function(arguments : Array[Value]) -> Value raise CasbinError {
let (key1, key2) = two_string_arguments("keyMatch", arguments)
Value::Bool(key_match(key1, key2))
}
///|
fn key_get_function(arguments : Array[Value]) -> Value raise CasbinError {
let (key1, key2) = two_string_arguments("keyGet", arguments)
Value::String(key_get(key1, key2))
}
///|
/// Casbin's `keyMatch`: `/foo/bar` matches `/foo/*`; a pattern without
/// `*` must equal the key.
fn key_match(key1 : String, key2 : String) -> Bool {
match key2.find("*") {
None => key1 == key2
Some(index) =>
if key1.length() > index {
key1[:index] == key2[:index]
} else {
key1[:] == key2[:index]
}
}
}
///|
/// Casbin's `keyGet`: the part of `key1` after the prefix of `key2` that
/// precedes its first `*`, or the empty string when there is no `*` or
/// the prefix does not match.
fn key_get(key1 : String, key2 : String) -> String {
match key2.find("*") {
None => ""
Some(index) =>
if key1.length() > index && key1[:index] == key2[:index] {
key1[index:].to_owned()
} else {
""
}
}
}
///|
fn two_string_arguments(
name : String,
arguments : Array[Value],
) -> (String, String) raise CasbinError {
if arguments.length() != 2 {
raise casbin_error(
MatcherEval,
name + ": expected 2 arguments, but got " + arguments.length().to_string(),
)
}
let first = match arguments[0] {
Value::String(value) => value
other =>
raise casbin_error(
MatcherEval,
name + ": argument must be a string, got " + other.type_name(),
)
}
let second = match arguments[1] {
Value::String(value) => value
other =>
raise casbin_error(
MatcherEval,
name + ": argument must be a string, got " + other.type_name(),
)
}
(first, second)
}