use std::io::{Read, stdin};
use std::num::NonZeroUsize;
use std::process::ExitCode;
use anyhow::Result;
use clap::Parser;
use regex::{Error as RegexError, RegexSet, RegexSetBuilder};
use serde::de::DeserializeSeed;
use serde_json::de::Deserializer;
use json_grep::{Callback, Path, PathCallback, Walker};
#[derive(Clone, Debug, Parser)]
#[command(name = "json-grep")]
struct Args {
#[arg()]
patterns: Vec<String>,
#[arg(short, long)]
count: bool,
#[arg(short, long)]
ignore_case: bool,
#[arg(short, long, value_name = "N")]
max_count: Option<NonZeroUsize>,
#[arg(short = 'v', long)]
invert_match: bool,
}
impl Args {
fn new_state<'a>(&self) -> Box<dyn Callback<'a> + 'a> {
if self.count {
self.with_base::<CountState>()
} else {
self.with_base::<State<'a>>()
}
}
fn with_base<'a, C: Callback<'a> + Default + 'a>(&self) -> Box<dyn Callback<'a> + 'a> {
match self.max_count {
Some(value) => Box::new((C::default(), MaxCountState::new(value))),
None => Box::<C>::default(),
}
}
}
impl TryFrom<&Args> for RegexSet {
type Error = RegexError;
fn try_from(value: &Args) -> Result<Self, Self::Error> {
RegexSetBuilder::new(&value.patterns)
.case_insensitive(value.ignore_case)
.build()
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct State<'a> {
has_match: bool,
path: Path<'a>,
}
impl<'a> PathCallback<'a> for State<'a> {
fn path(&mut self) -> &mut Path<'a> {
&mut self.path
}
fn on_match(&mut self, matched: &str) -> bool {
self.has_match = true;
println!("{path}: {matched}", path = &self.path);
false
}
fn on_finish(&mut self) -> bool {
!self.has_match
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct CountState {
count: usize,
}
impl Callback<'_> for CountState {
fn on_match(&mut self, _matched: &str) -> bool {
self.count += 1;
false
}
fn on_finish(&mut self) -> bool {
println!("{}", self.count);
self.count == 0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct MaxCountState {
left: usize,
}
impl MaxCountState {
fn new(left: NonZeroUsize) -> Self {
Self { left: left.get() }
}
}
impl Callback<'_> for MaxCountState {
fn on_match(&mut self, _matched: &str) -> bool {
self.left -= 1;
self.left == 0
}
}
fn main() -> Result<ExitCode> {
let args = Args::parse();
let mut input = Vec::new();
stdin().read_to_end(&mut input)?;
let mut deserializer = Deserializer::from_slice(&input);
let mut state = args.new_state();
let mut walker = Walker::new((&args).try_into()?, args.invert_match, state.as_mut());
walker.deserialize(&mut deserializer)?;
if state.on_finish() {
Ok(ExitCode::FAILURE)
} else {
Ok(ExitCode::SUCCESS)
}
}