use crate::args::ExploreArgs;
use crate::utils::{print_diagnostics, read_input};
use jsonette::JsonNode;
use regex::Regex;
pub fn handle_explore(mut args: ExploreArgs) {
if args.file.is_none() {
let path_as_file = std::path::Path::new(&args.path);
if path_as_file.exists() && path_as_file.is_file() {
args.file = Some(path_as_file.to_path_buf());
args.path = "$".to_string();
}
}
let (input, label) = match read_input(&args.file) {
Ok(res) => res,
Err(e) => {
eprintln!("Error reading input: {}", e);
std::process::exit(1);
}
};
let node = match jsonette::parse(&input) {
Ok(node) => node,
Err(diags) => {
print_diagnostics(&input, &diags, &label);
std::process::exit(1);
}
};
let path_diags = jsonette::diagnostics_for_path(&args.path);
if !path_diags.is_empty() {
for diag in &path_diags {
eprintln!("Error: {}", diag.message);
}
std::process::exit(1);
}
match jsonette::evaluate_path(&node, &args.path) {
Ok(matches) => {
if matches.is_empty() {
println!("No nodes matched the path.");
return;
}
let mut output = String::new();
for (i, match_node) in matches.iter().enumerate() {
if matches.len() > 1 {
output.push_str(&format!("--- Match {} ---\n", i + 1));
}
match match_node {
JsonNode::Array(elements, _) => {
output.push_str(&format!("Length: {} elements\n", elements.len()));
if !elements.is_empty() {
output.push_str(&format!(
"(indices [0] through [{}])\n",
elements.len() - 1
));
}
}
JsonNode::Object(pairs, _) => {
let mut keys: Vec<String> = pairs.iter().map(|kv| kv.key.clone()).collect();
if let Some(grep_str) = &args.grep {
keys.retain(|k| k.contains(grep_str));
}
if let Some(regex_str) = &args.regex {
if let Ok(re) = Regex::new(regex_str) {
keys.retain(|k| re.is_match(k));
} else {
eprintln!("Error: Invalid regex '{}'", regex_str);
std::process::exit(1);
}
}
keys.sort();
let total_matched = keys.len();
if let Some(limit) = args.limit {
keys.truncate(limit);
}
if keys.is_empty() {
output.push_str("No keys found (or all filtered out).\n");
} else {
for key in &keys {
output.push_str(&format!("{}\n", key));
}
if total_matched > keys.len() {
output.push_str(&format!(
"... and {} more keys\n",
total_matched - keys.len()
));
}
}
}
_ => {
output.push_str(&format!(
"Matched a {} node. Use 'query' to see its value.\n",
match_node.node_type()
));
}
}
if matches.len() > 1 && i < matches.len() - 1 {
output.push('\n');
}
}
page_output(&output);
}
Err(err) => {
eprintln!("Error evaluating JSONPath: {}", err);
std::process::exit(1);
}
}
}
fn page_output(output: &str) {
use std::io::{IsTerminal, Write};
use std::process::{Command, Stdio};
if std::io::stdout().is_terminal()
&& let Ok(mut child) = Command::new("less")
.args(["-F", "-R", "-X"])
.stdin(Stdio::piped())
.spawn()
{
if let Some(mut stdin) = child.stdin.take() {
let _ = stdin.write_all(output.as_bytes());
}
let _ = child.wait();
return;
}
print!("{}", output);
}