use super::autocomplete_state::{Suggestion, SuggestionType};
use std::collections::HashSet;
use std::sync::LazyLock;
#[derive(Debug, Clone)]
pub struct JqFunction {
pub name: &'static str,
pub signature: &'static str,
pub description: &'static str,
pub needs_parens: bool,
}
impl JqFunction {
pub const fn new(
name: &'static str,
signature: &'static str,
description: &'static str,
needs_parens: bool,
) -> Self {
Self {
name,
signature,
description,
needs_parens,
}
}
}
pub static JQ_FUNCTION_METADATA: &[JqFunction] = &[
JqFunction::new("map", "map(expr)", "Apply expression to each element", true),
JqFunction::new(
"select",
"select(expr)",
"Filter elements by condition",
true,
),
JqFunction::new("sort_by", "sort_by(expr)", "Sort array by expression", true),
JqFunction::new(
"group_by",
"group_by(expr)",
"Group array elements by expression",
true,
),
JqFunction::new(
"unique_by",
"unique_by(expr)",
"Remove duplicates by expression",
true,
),
JqFunction::new("min_by", "min_by(expr)", "Minimum by expression", true),
JqFunction::new("max_by", "max_by(expr)", "Maximum by expression", true),
JqFunction::new("limit", "limit(num; expr)", "Limit output count", true),
JqFunction::new("nth", "nth(num)", "Nth element", true),
JqFunction::new("range", "range(num)", "Generate range", true),
JqFunction::new(
"until",
"until(cond; update)",
"Repeat until condition",
true,
),
JqFunction::new(
"while",
"while(cond; update)",
"Repeat while condition",
true,
),
JqFunction::new("recurse", "recurse(expr)", "Apply recursively", true),
JqFunction::new(
"walk",
"walk(expr)",
"Apply to all values recursively",
true,
),
JqFunction::new(
"with_entries",
"with_entries(expr)",
"Transform object entries",
true,
),
JqFunction::new("has", "has(key)", "Check if key exists", true),
JqFunction::new("del", "del(path)", "Delete key/path", true),
JqFunction::new("getpath", "getpath(path)", "Get value at path", true),
JqFunction::new("setpath", "setpath(path; val)", "Set value at path", true),
JqFunction::new("delpaths", "delpaths(paths)", "Delete multiple paths", true),
JqFunction::new("split", "split(str)", "Split string by delimiter", true),
JqFunction::new("join", "join(str)", "Join array with delimiter", true),
JqFunction::new("ltrimstr", "ltrimstr(str)", "Remove prefix string", true),
JqFunction::new("rtrimstr", "rtrimstr(str)", "Remove suffix string", true),
JqFunction::new(
"startswith",
"startswith(str)",
"Check if starts with value",
true,
),
JqFunction::new(
"endswith",
"endswith(str)",
"Check if ends with value",
true,
),
JqFunction::new("test", "test(regex)", "Test regex match", true),
JqFunction::new("match", "match(regex)", "Match regex", true),
JqFunction::new("capture", "capture(regex)", "Capture regex groups", true),
JqFunction::new("scan", "scan(regex)", "Scan for all regex matches", true),
JqFunction::new("splits", "splits(regex)", "Split by regex", true),
JqFunction::new("sub", "sub(regex; str)", "Replace first regex match", true),
JqFunction::new(
"gsub",
"gsub(regex; str)",
"Replace all regex matches",
true,
),
JqFunction::new("contains", "contains(val)", "Check if contains value", true),
JqFunction::new(
"inside",
"inside(val)",
"Check if element is inside array",
true,
),
JqFunction::new("index", "index(val)", "Find first index of value", true),
JqFunction::new("rindex", "rindex(val)", "Find last index of value", true),
JqFunction::new("indices", "indices(val)", "Find all indices of value", true),
JqFunction::new("strftime", "strftime(fmt)", "Format timestamp", true),
JqFunction::new("strptime", "strptime(fmt)", "Parse timestamp", true),
JqFunction::new(
"fromdate",
"fromdate",
"Parse ISO 8601 date to timestamp",
false,
),
JqFunction::new("todate", "todate", "Format timestamp as ISO 8601", false),
JqFunction::new("keys", "keys", "Get object keys or array indices", false),
JqFunction::new(
"keys_unsorted",
"keys_unsorted",
"Get object keys (unsorted)",
false,
),
JqFunction::new("values", "values", "Get all values", false),
JqFunction::new("sort", "sort", "Sort array", false),
JqFunction::new("reverse", "reverse", "Reverse array", false),
JqFunction::new("unique", "unique", "Remove duplicate values", false),
JqFunction::new("flatten", "flatten", "Flatten nested arrays", false),
JqFunction::new("add", "add", "Sum array elements or concatenate", false),
JqFunction::new("length", "length", "Length of array/object/string", false),
JqFunction::new("first", "first", "First element", false),
JqFunction::new("last", "last", "Last element", false),
JqFunction::new("min", "min", "Minimum value", false),
JqFunction::new("max", "max", "Maximum value", false),
JqFunction::new("transpose", "transpose", "Transpose matrix", false),
JqFunction::new(
"to_entries",
"to_entries",
"Convert object to key-value pairs",
false,
),
JqFunction::new(
"from_entries",
"from_entries",
"Convert key-value pairs to object",
false,
),
JqFunction::new("paths", "paths", "Get all paths (leaf paths)", false),
JqFunction::new("leaf_paths", "leaf_paths", "Get all leaf paths", false),
JqFunction::new("type", "type", "Get value type", false),
JqFunction::new("tostring", "tostring", "Convert to string", false),
JqFunction::new("tonumber", "tonumber", "Convert to number", false),
JqFunction::new("tojson", "tojson", "Convert value to JSON string", false),
JqFunction::new("fromjson", "fromjson", "Parse JSON string to value", false),
JqFunction::new("arrays", "arrays", "Select arrays", false),
JqFunction::new("objects", "objects", "Select objects", false),
JqFunction::new("iterables", "iterables", "Select arrays/objects", false),
JqFunction::new("booleans", "booleans", "Select booleans", false),
JqFunction::new("numbers", "numbers", "Select numbers", false),
JqFunction::new("strings", "strings", "Select strings", false),
JqFunction::new("nulls", "nulls", "Select nulls", false),
JqFunction::new("scalars", "scalars", "Select non-iterable values", false),
JqFunction::new("floor", "floor", "Round down", false),
JqFunction::new("ceil", "ceil", "Round up", false),
JqFunction::new("round", "round", "Round to nearest", false),
JqFunction::new("sqrt", "sqrt", "Square root", false),
JqFunction::new("abs", "abs", "Absolute value", false),
JqFunction::new("now", "now", "Current Unix timestamp", false),
JqFunction::new("empty", "empty", "Produce no output", false),
JqFunction::new("error", "error", "Raise error", false),
JqFunction::new("not", "not", "Logical NOT", false),
JqFunction::new(
"ascii_downcase",
"ascii_downcase",
"Convert to lowercase",
false,
),
JqFunction::new(
"ascii_upcase",
"ascii_upcase",
"Convert to uppercase",
false,
),
JqFunction::new("env", "env", "Access environment variables", false),
];
pub static ELEMENT_CONTEXT_FUNCTIONS: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
[
"map",
"select",
"sort_by",
"group_by",
"unique_by",
"min_by",
"max_by",
"recurse",
"walk",
]
.into_iter()
.collect()
});
pub fn is_element_context_function(name: &str) -> bool {
ELEMENT_CONTEXT_FUNCTIONS.contains(name)
}
static JQ_BUILTINS: LazyLock<Vec<Suggestion>> = LazyLock::new(|| {
let mut builtins = Vec::new();
builtins.extend(vec![
Suggestion::new(".[]", SuggestionType::Pattern)
.with_description("Iterate over array/object values"),
Suggestion::new(".[0]", SuggestionType::Pattern).with_description("First array element"),
Suggestion::new(".[-1]", SuggestionType::Pattern).with_description("Last array element"),
Suggestion::new("..", SuggestionType::Pattern)
.with_description("Recursive descent (all values)"),
]);
builtins.extend(vec![
Suggestion::new("|", SuggestionType::Operator).with_description("Pipe operator"),
Suggestion::new("//", SuggestionType::Operator)
.with_description("Alternative operator (default value)"),
Suggestion::new("and", SuggestionType::Operator).with_description("Logical AND"),
Suggestion::new("or", SuggestionType::Operator).with_description("Logical OR"),
]);
for func in JQ_FUNCTION_METADATA {
builtins.push(
Suggestion::new(func.name, SuggestionType::Function)
.with_description(func.description)
.with_signature(func.signature)
.with_needs_parens(func.needs_parens),
);
}
builtins.extend(vec![
Suggestion::new("@json", SuggestionType::Function)
.with_description("Format as JSON string"),
Suggestion::new("@uri", SuggestionType::Function).with_description("URL encode"),
Suggestion::new("@csv", SuggestionType::Function).with_description("Format as CSV"),
Suggestion::new("@tsv", SuggestionType::Function).with_description("Format as TSV"),
Suggestion::new("@html", SuggestionType::Function).with_description("HTML encode"),
Suggestion::new("@base64", SuggestionType::Function).with_description("Base64 encode"),
Suggestion::new("@base64d", SuggestionType::Function).with_description("Base64 decode"),
]);
builtins.extend(vec![
Suggestion::new("fromdateiso8601", SuggestionType::Function)
.with_description("Parse ISO8601 date"),
Suggestion::new("todateiso8601", SuggestionType::Function)
.with_description("Format as ISO8601 date"),
]);
builtins.extend(vec![
Suggestion::new("if", SuggestionType::Function).with_description("Conditional expression"),
Suggestion::new("then", SuggestionType::Function).with_description("Then clause"),
Suggestion::new("else", SuggestionType::Function).with_description("Else clause"),
Suggestion::new("elif", SuggestionType::Function).with_description("Else-if clause"),
Suggestion::new("end", SuggestionType::Function).with_description("End block"),
]);
builtins.extend(vec![
Suggestion::new("in", SuggestionType::Function)
.with_description("Check if value is in object"),
Suggestion::new("as", SuggestionType::Function).with_description("Bind variable"),
Suggestion::new("repeat", SuggestionType::Function)
.with_description("Repeat expression infinitely"),
Suggestion::new("$ENV", SuggestionType::Function).with_description("Environment object"),
]);
builtins.extend(vec![
Suggestion::new("|=", SuggestionType::Operator).with_description("Update assignment"),
Suggestion::new("+=", SuggestionType::Operator).with_description("Addition assignment"),
Suggestion::new("-=", SuggestionType::Operator).with_description("Subtraction assignment"),
Suggestion::new("*=", SuggestionType::Operator)
.with_description("Multiplication assignment"),
Suggestion::new("/=", SuggestionType::Operator).with_description("Division assignment"),
Suggestion::new("//=", SuggestionType::Operator).with_description("Alternative assignment"),
]);
builtins
});
pub fn filter_builtins(prefix: &str) -> Vec<Suggestion> {
if prefix.is_empty() {
return Vec::new();
}
let prefix_lower = prefix.to_lowercase();
JQ_BUILTINS
.iter()
.filter(|s| s.text.to_lowercase().starts_with(&prefix_lower))
.cloned()
.collect()
}
#[cfg(test)]
pub fn get_all_function_metadata() -> &'static [JqFunction] {
JQ_FUNCTION_METADATA
}
#[cfg(test)]
#[path = "jq_functions_tests.rs"]
mod jq_functions_tests;