#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Category {
StringProcessing,
DataParsing,
SafetyFunctions,
ArrayFunctions,
Utility,
}
impl Category {
#[allow(dead_code)] pub fn as_str(&self) -> &'static str {
match self {
Category::StringProcessing => "STRING PROCESSING",
Category::DataParsing => "DATA PARSING",
Category::SafetyFunctions => "SAFETY FUNCTIONS",
Category::ArrayFunctions => "ARRAY FUNCTIONS",
Category::Utility => "UTILITY",
}
}
#[allow(dead_code)] pub fn sort_order(&self) -> u8 {
match self {
Category::StringProcessing => 1,
Category::DataParsing => 2,
Category::SafetyFunctions => 3,
Category::ArrayFunctions => 4,
Category::Utility => 5,
}
}
}
#[allow(dead_code)] #[derive(Debug, Clone)]
pub struct FunctionDoc {
pub signature: &'static str,
pub doc: &'static str,
pub category: Category,
}
#[allow(dead_code)] pub fn get_all_function_docs() -> Vec<FunctionDoc> {
let mut docs = Vec::new();
docs.extend(vec![
FunctionDoc {
signature: "text.after(delimiter)",
doc: "Text after first delimiter",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.before(delimiter)",
doc: "Text before first delimiter",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.between(start, end)",
doc: "Text between start and end delimiters",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.contains(pattern)",
doc: "Check if text contains pattern",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.count(pattern)",
doc: "Count occurrences of pattern in text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.ending_with(suffix)",
doc: "Return text if it ends with suffix",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.extract_domain()",
doc: "Extract domain from URL or email",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.extract_ip()",
doc: "Extract first IP address from text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.extract_ips()",
doc: "Extract all IP addresses from text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.extract_re(pattern [, group])",
doc: "Extract regex match/group from text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.extract_all_re(pattern [, group])",
doc: "Extract all regex matches from text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.extract_url()",
doc: "Extract first URL from text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.is_digit()",
doc: "Check if text contains only digits",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.is_private_ip()",
doc: "Check if IP address is in private range",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.lower()",
doc: "Convert text to lowercase",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.mask_ip([octets])",
doc: "Mask IP address (default: last octet)",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.matches(pattern)",
doc: "Check if text matches regex pattern",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.replace_re(pattern, replacement)",
doc: "Replace regex matches with replacement",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.slice(spec)",
doc: "Slice text using Python-style notation",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.split_re(pattern)",
doc: "Split text by regex pattern",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.starting_with(prefix)",
doc: "Return text if it starts with prefix",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.strip([chars])",
doc: "Remove whitespace or specified chars",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.upper()",
doc: "Convert text to uppercase",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.decode_b64()",
doc: "Decode base64 string to text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.decode_hex()",
doc: "Decode hexadecimal string to text",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.decode_url()",
doc: "Decode URL-encoded string",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.encode_b64()",
doc: "Encode text to base64 string",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.encode_hex()",
doc: "Encode text to hexadecimal string",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.encode_url()",
doc: "URL-encode text (percent encoding)",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.escape_html()",
doc: "Escape HTML special characters",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.escape_json()",
doc: "Escape JSON special characters",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.unescape_html()",
doc: "Unescape HTML entities",
category: Category::StringProcessing,
},
FunctionDoc {
signature: "text.unescape_json()",
doc: "Unescape JSON escape sequences",
category: Category::StringProcessing,
},
]);
docs.extend(vec![
FunctionDoc {
signature: "parse_kv(text [, sep [, kv_sep]])",
doc: "Parse key-value pairs from text",
category: Category::DataParsing,
},
FunctionDoc {
signature: "to_float(text)",
doc: "Convert text to float (0 on error)",
category: Category::DataParsing,
},
FunctionDoc {
signature: "to_int(text)",
doc: "Convert text to integer (0 on error)",
category: Category::DataParsing,
},
FunctionDoc {
signature: "map.unflatten([separator])",
doc: "Reconstruct nested structures from flat keys",
category: Category::DataParsing,
},
]);
docs.extend(vec![
FunctionDoc {
signature: "contains_any(array, search_array)",
doc: "Check if array contains any search values",
category: Category::ArrayFunctions,
},
FunctionDoc {
signature: "array.flatten([style [, max_depth]])",
doc: "Flatten nested arrays/objects",
category: Category::ArrayFunctions,
},
FunctionDoc {
signature: "array.join(separator)",
doc: "Join array elements with separator",
category: Category::ArrayFunctions,
},
FunctionDoc {
signature: "reversed(array)",
doc: "Return new array in reverse order",
category: Category::ArrayFunctions,
},
FunctionDoc {
signature: "sorted(array)",
doc: "Return new sorted array",
category: Category::ArrayFunctions,
},
FunctionDoc {
signature: "sorted_by(array, field)",
doc: "Sort array of objects by field",
category: Category::ArrayFunctions,
},
FunctionDoc {
signature: "starts_with_any(array, search_array)",
doc: "Check if array starts with any search values",
category: Category::ArrayFunctions,
},
]);
docs.extend(vec![
FunctionDoc {
signature: "path_equals(obj, \"field.path\", value)",
doc: "Safe nested field comparison",
category: Category::SafetyFunctions,
},
FunctionDoc {
signature: "to_bool(value, default)",
doc: "Safe boolean conversion with fallback",
category: Category::SafetyFunctions,
},
FunctionDoc {
signature: "to_number(value, default)",
doc: "Safe number conversion with fallback",
category: Category::SafetyFunctions,
},
]);
docs.extend(vec![
FunctionDoc {
signature: "eprint(message)",
doc: "Print to stderr",
category: Category::Utility,
},
FunctionDoc {
signature: "print(message)",
doc: "Print to stdout",
category: Category::Utility,
},
]);
docs
}
#[allow(dead_code)] pub fn generate_help_text() -> String {
let docs = get_all_function_docs();
let mut output = String::from("\nAvailable Rhai Functions for Kelora:\n\n");
let mut categories: std::collections::BTreeMap<u8, Vec<&FunctionDoc>> =
std::collections::BTreeMap::new();
for doc in &docs {
categories
.entry(doc.category.sort_order())
.or_default()
.push(doc);
}
for (_, category_docs) in categories {
if category_docs.is_empty() {
continue;
}
let category_name = category_docs[0].category.as_str();
output.push_str(&format!("{}:\n", category_name));
let mut sorted_docs = category_docs;
sorted_docs.sort_by(|a, b| a.signature.cmp(b.signature));
for doc in sorted_docs {
output.push_str(&format!(" {:<35} {}\n", doc.signature, doc.doc));
}
output.push('\n');
}
output.push_str("Note: These functions are available in --filter and --exec expressions.\n");
output.push_str("Use 'e' to access the current event (e.g., e.message.lower()).\n");
output.push_str("For more examples, see the documentation or use --help for general usage.\n");
output
}