use anyhow::{anyhow, Result};
use clap::{ArgAction, Parser, ValueEnum};
use cql2::{Expr, Validator};
use std::io::Read;
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
pub struct Cli {
input: Option<String>,
#[arg(short, long)]
input_format: Option<InputFormat>,
#[arg(short, long)]
output_format: Option<OutputFormat>,
#[arg(long, default_value_t = true, action = ArgAction::Set)]
validate: bool,
#[arg(short, long, action = ArgAction::Count)]
verbose: u8,
}
#[derive(Debug, ValueEnum, Clone)]
pub enum InputFormat {
Json,
Text,
}
#[derive(Debug, ValueEnum, Clone)]
enum OutputFormat {
JsonPretty,
Json,
Text,
Sql,
}
impl Cli {
pub fn run(self) {
if let Err(err) = self.run_inner() {
eprintln!("{}", err);
std::process::exit(1)
}
}
pub fn run_inner(self) -> Result<()> {
let input = self
.input
.and_then(|input| if input == "-" { None } else { Some(input) })
.map(Ok)
.unwrap_or_else(read_stdin)?;
let input_format = self.input_format.unwrap_or_else(|| {
if input.starts_with('{') {
InputFormat::Json
} else {
InputFormat::Text
}
});
let expr: Expr = match input_format {
InputFormat::Json => cql2::parse_json(&input)?,
InputFormat::Text => match cql2::parse_text(&input) {
Ok(expr) => expr,
Err(err) => {
return Err(anyhow!("[ERROR] Parsing error: {input}\n{err}"));
}
},
};
if self.validate {
let validator = Validator::new().unwrap();
let value = serde_json::to_value(&expr).unwrap();
if let Err(error) = validator.validate(&value) {
return Err(anyhow!(
"[ERROR] Invalid CQL2: {input}\n{}",
match self.verbose {
0 => "For more detailed validation information, use -v".to_string(),
1 => format!("For more detailed validation information, use -vv\n{error}"),
2 =>
format!("For more detailed validation information, use -vvv\n{error:#}"),
_ => {
let detailed_output = error.detailed_output();
format!("{detailed_output:#}")
}
}
));
}
}
let output_format = self.output_format.unwrap_or(match input_format {
InputFormat::Json => OutputFormat::Json,
InputFormat::Text => OutputFormat::Text,
});
match output_format {
OutputFormat::JsonPretty => serde_json::to_writer_pretty(std::io::stdout(), &expr)?,
OutputFormat::Json => serde_json::to_writer(std::io::stdout(), &expr)?,
OutputFormat::Text => print!("{}", expr.to_text()?),
OutputFormat::Sql => serde_json::to_writer_pretty(std::io::stdout(), &expr.to_sql()?)?,
}
println!();
Ok(())
}
}
fn read_stdin() -> Result<String> {
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
Ok(buf)
}