mod additional_help;
#[cfg(feature = "create-docs")]
mod build_docs;
mod const_getter;
mod duplication_remover;
mod extractor;
mod filter;
mod functions;
mod functions_definitions;
mod grouper;
mod input_context_extractor;
mod json_parser;
mod json_value;
mod limits;
mod merger;
mod output_style;
mod pre_sets;
mod processor;
mod reader;
mod regex_cache;
mod selection;
mod selection_extractor;
#[cfg(feature = "create-docs")]
mod selection_help;
mod sorters;
mod splitter;
mod variables_extractor;
use additional_help::display_additional_help;
use clap::Parser;
use duplication_remover::Uniqueness;
use filter::Filter;
use grouper::Grouper;
use json_parser::JsonParserError;
use json_value::JsonValue;
use limits::Limiter;
use merger::Merger;
use output_style::OutputOptions;
use output_style::OutputStyleValidationError;
use pre_sets::PreSetCollection;
use pre_sets::PreSetParserError;
use processor::ProcessDecision;
use processor::{Context, Process, ProcessError, Titles};
use regex_cache::RegexCache;
use selection::Selection;
use selection::SelectionParseError;
use sorters::Sorter;
use sorters::SorterParserError;
use splitter::Splitter;
use std::cell::RefCell;
use std::fmt::Error as FormatError;
use std::fs::read_dir;
use std::io::Error as IoError;
use std::io::Read;
use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
use thiserror::Error;
use crate::additional_help::create_possible_values;
use crate::json_parser::JsonParser;
use crate::reader::{Reader, from_file, from_std_in};
#[derive(Parser)]
#[command(author, version, about)]
pub struct Cli {
files: Vec<PathBuf>,
#[arg(long, default_value_t = OnError::Ignore)]
#[clap(value_enum)]
on_error: OnError,
#[arg(long, short, visible_alias = "select")]
choose: Vec<String>,
#[arg(long, short, visible_alias = "where")]
filter: Option<String>,
#[arg(long, short, visible_alias = "split-by")]
break_by: Option<String>,
#[arg(long, short, visible_alias = "combine", visible_alias = "merge")]
group_by: Option<Option<String>>,
#[arg(long, short, visible_alias = "order-by")]
sort_by: Vec<String>,
#[arg(long, short = 'k', default_value_t = 0)]
skip: u64,
#[arg(long, short, visible_alias = "limit")]
take: Option<u64>,
#[arg(
long,
short,
default_value = None,
value_parser = create_possible_values(),
ignore_case = true
)]
additional_help: Option<String>,
#[arg(long, short)]
unique: bool,
#[arg(long, short = 'e')]
set: Vec<String>,
#[arg(long, default_value_t = 0)]
regular_expression_cache_size: usize,
#[arg(long)]
only_objects_and_arrays: bool,
#[command(flatten)]
output_options: OutputOptions,
}
#[derive(clap::ValueEnum, Debug, Clone, PartialEq)]
#[clap(rename_all = "kebab_case")]
enum OnError {
Ignore,
Panic,
Stderr,
Stdout,
}
pub fn go<R: Read>(
cli: Cli,
stdout: Rc<RefCell<dyn std::io::Write + Send>>,
stderr: Rc<RefCell<dyn std::io::Write + Send>>,
stdin: Box<dyn Fn() -> R>,
) -> Result<()> {
let master = Master::new(cli, stdout, stderr, stdin);
master.go()
}
struct Master<R: Read> {
cli: Cli,
stdout: Rc<RefCell<dyn std::io::Write + Send>>,
stderr: Rc<RefCell<dyn std::io::Write + Send>>,
stdin: Box<dyn Fn() -> R>,
regular_expression_cache: RegexCache,
}
impl<S: Read> Master<S> {
pub fn new(
cli: Cli,
stdout: Rc<RefCell<dyn std::io::Write + Send>>,
stderr: Rc<RefCell<dyn std::io::Write + Send>>,
stdin: Box<dyn Fn() -> S>,
) -> Self {
let regular_expression_cache = RegexCache::new(cli.regular_expression_cache_size);
Master {
cli,
stdout,
stderr,
stdin,
regular_expression_cache,
}
}
pub fn go(&self) -> Result<()> {
if let Some(help_type) = &self.cli.additional_help {
display_additional_help(help_type);
return Ok(());
}
let mut process = self.cli.output_options.get_processor(self.stdout.clone())?;
if let Some(group_by) = &self.cli.group_by {
if let Some(group_by) = group_by {
let group_by = Grouper::from_str(group_by)?;
process = group_by.create_process(process);
} else {
process = Merger::create_process(process);
}
}
process = Limiter::create_process(self.cli.skip, self.cli.take, process);
for sorter in &self.cli.sort_by {
let sorter = Sorter::from_str(sorter)?;
let max_size = self.cli.take.map(|take| (self.cli.skip + take) as usize);
process = sorter.create_processor(process, max_size);
}
if self.cli.unique {
process = Uniqueness::create_process(process);
}
for selection in self.cli.choose.iter().rev() {
let selection = Selection::from_str(selection)?;
process = selection.create_process(process);
}
if let Some(filter) = &self.cli.filter {
let filter = Filter::from_str(filter)?;
process = filter.create_process(process);
}
if let Some(splitter) = &self.cli.break_by {
let splitter = Splitter::from_str(splitter)?;
process = splitter.create_process(process);
}
process = self.cli.set.create_process(process)?;
process.start(Titles::default())?;
let mut index = 0;
if self.cli.files.is_empty() {
let mut reader = from_std_in((self.stdin)());
self.read_input(&mut reader, &mut index, process.as_mut())?;
} else {
for file in self.cli.files.clone() {
self.read_file(&file, &mut index, process.as_mut())?;
}
}
process.complete()?;
Ok(())
}
fn read_file(&self, file: &PathBuf, index: &mut u64, process: &mut dyn Process) -> Result<()> {
assert!(file.exists(), "File {file:?} not exists");
if file.is_dir() {
for entry in read_dir(file)? {
let path = entry?.path();
self.read_file(&path, index, process)?;
}
} else {
let mut reader = from_file(file)?;
self.read_input(&mut reader, index, process)?;
}
Ok(())
}
fn read_input<R: Read>(
&self,
reader: &mut Reader<R>,
index: &mut u64,
process: &mut dyn Process,
) -> Result<()> {
let mut in_file_index: u64 = 0;
loop {
let started = reader.where_am_i();
match reader.next_json_value() {
Ok(Some(val)) => {
if self.cli.only_objects_and_arrays {
match val {
JsonValue::Object(_) | JsonValue::Array(_) => {}
_ => {
continue;
}
}
}
let ended = reader.where_am_i();
let context = Context::new_with_input(
val,
started,
ended,
in_file_index,
*index,
&self.regular_expression_cache,
);
match process.process(context)? {
ProcessDecision::Break => {
break Ok(());
}
ProcessDecision::Continue => {
in_file_index += 1;
*index += 1;
}
}
}
Ok(None) => {
return Ok(());
}
Err(e) => {
if !e.can_recover() {
return Err(e.into());
}
match self.cli.on_error {
OnError::Ignore => {}
OnError::Panic => {
return Err(e.into());
}
OnError::Stdout => writeln!(self.stdout.borrow_mut(), "error:{e}")?,
OnError::Stderr => writeln!(self.stderr.borrow_mut(), "error:{e}")?,
}
}
};
}
}
}
pub type Result<T> = std::result::Result<T, MainError>;
#[derive(Debug, Error)]
pub enum MainError {
#[error("{0}")]
Json(#[from] JsonParserError),
#[error("{0}")]
Format(#[from] FormatError),
#[error("{0}")]
SelectionParse(#[from] SelectionParseError),
#[error("{0}")]
SorterParse(#[from] SorterParserError),
#[error("{0}")]
Io(#[from] IoError),
#[error("{0}")]
Processor(#[from] ProcessError),
#[error("{0}")]
PreSet(#[from] PreSetParserError),
#[error("{0}")]
OutputStyle(#[from] OutputStyleValidationError),
}