use clap::Parser;
use json_colorizer::{format_json, format_json_compact, query, FormatOptions, Theme};
use serde_json::Value;
use std::fs;
use std::io::{self, Read, IsTerminal};
use std::process;
#[derive(Parser)]
#[command(name = "jfmt", version, about)]
struct Args {
#[arg(short, long)]
compact: bool,
#[arg(short, long)]
query: Option<String>,
#[arg(short = 'S', long)]
sort_keys: bool,
#[arg(short = 'r', long)]
raw: bool,
#[arg(short = 'i', long, default_value = "2")]
indent: usize,
file: Option<String>,
}
fn main() {
let args = Args::parse();
let input = match &args.file {
Some(path) => fs::read_to_string(path).unwrap_or_else(|e| {
eprintln!("error: failed to read file '{}': {}", path, e);
process::exit(1);
}),
None => {
let mut buf = String::new();
io::stdin().read_to_string(&mut buf).unwrap_or_else(|e| {
eprintln!("error: failed to read stdin: {}", e);
process::exit(1);
});
buf
}
};
if input.trim().is_empty() {
return;
}
let stream = serde_json::Deserializer::from_str(&input).into_iter::<Value>();
for (obj_idx, value_res) in stream.enumerate() {
let value = value_res.unwrap_or_else(|e| {
eprintln!("error: invalid JSON (object {}): {}", obj_idx + 1, e);
process::exit(1);
});
let results = match &args.query {
Some(q) => query(&value, q).unwrap_or_else(|e| {
eprintln!("error: {}", e);
process::exit(1);
}),
None => vec![&value],
};
let opts = FormatOptions {
indent: args.indent,
color: if args.raw { false } else { io::stdout().is_terminal() },
sort_keys: args.sort_keys,
theme: Theme::default(),
};
for (res_idx, res) in results.iter().enumerate() {
if args.raw {
if let Some(s) = res.as_str() {
println!("{}", s);
} else {
println!("{}", format_json_compact(res));
}
} else if args.compact {
println!("{}", format_json_compact(res));
} else {
if results.len() > 1 && res_idx > 0 {
}
println!("{}", format_json(res, &opts));
}
}
}
}