use crate::json_node::JsonNode;
use crate::types::CompletionItem;
pub fn completions_at(node: &JsonNode, path_prefix: &str) -> Vec<CompletionItem> {
let (parent_path, prefix) = match path_prefix.rfind('.') {
Some(idx) => {
let parent = &path_prefix[..idx];
let pref = &path_prefix[idx + 1..];
let parent = if parent.is_empty() { "$" } else { parent };
(parent, pref)
}
None => {
if path_prefix == "$" {
("$", "")
} else {
return vec![];
}
}
};
let mut completions = Vec::new();
if let Ok(results) = crate::query::evaluate_path(node, parent_path) {
for result in results {
if let JsonNode::Object(pairs, _) = result {
for kv in pairs {
if kv.key.starts_with(prefix) {
let new_path = if parent_path == "$" {
format!("$.{}", kv.key)
} else {
format!("{}.{}", parent_path, kv.key)
};
completions.push(CompletionItem {
key: kv.key.clone(),
path: new_path,
});
}
}
}
}
}
completions.sort_by(|a, b| a.key.cmp(&b.key));
completions.dedup_by(|a, b| a.key == b.key);
completions
}
#[cfg(test)]
mod stub_tests {
use super::*;
use crate::parser::parse;
#[test]
fn test_completions_at_root() {
let node = parse(r#"{"name": "Alice", "age": 30}"#).unwrap();
let comps = completions_at(&node, "$.");
assert_eq!(comps.len(), 2);
assert_eq!(comps[0].key, "age");
assert_eq!(comps[1].key, "name");
}
#[test]
fn test_completions_at_prefix() {
let node = parse(r#"{"name": "Alice", "age": 30, "nested": {"nav": 1}}"#).unwrap();
let comps = completions_at(&node, "$.na");
assert_eq!(comps.len(), 1);
assert_eq!(comps[0].key, "name");
assert_eq!(comps[0].path, "$.name");
}
#[test]
fn test_completions_nested() {
let node = parse(r#"{"nested": {"name": "Alice", "nav": 1}}"#).unwrap();
let comps = completions_at(&node, "$.nested.na");
assert_eq!(comps.len(), 2);
assert_eq!(comps[0].key, "name");
assert_eq!(comps[0].path, "$.nested.name");
assert_eq!(comps[1].key, "nav");
assert_eq!(comps[1].path, "$.nested.nav");
}
}