-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
221 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ name = "cql2-cli" | |
version = "0.1.0" | ||
authors = ["David Bitner <[email protected]>"] | ||
edition = "2021" | ||
description = "Command line interface (CLI) for Common Query Language (CQL2)" | ||
description = "Command line interface for Common Query Language (CQL2)" | ||
readme = "README.md" | ||
homepage = "https://github.com/developmentseed/cql2-rs" | ||
repository = "https://github.com/developmentseed/cql2-rs" | ||
|
@@ -12,8 +12,9 @@ keywords = ["cql2"] | |
|
||
|
||
[dependencies] | ||
anyhow = "1.0" | ||
clap = { workspace = true, features = ["derive"] } | ||
cql2 = { path = "..", version = "0.1.0" } | ||
clap = { version = "4.5", features = ["derive"] } | ||
serde_json = "1.0" | ||
|
||
[[bin]] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
use anyhow::{anyhow, Result}; | ||
use clap::{ArgAction, Parser, ValueEnum}; | ||
use cql2::{Expr, Validator}; | ||
use std::io::Read; | ||
|
||
/// The CQL2 command-line interface. | ||
#[derive(Debug, Parser)] | ||
#[command(version, about, long_about = None)] | ||
pub struct Cli { | ||
/// The input CQL2 | ||
/// | ||
/// If not provided, or `-`, the CQL2 will be read from standard input. The | ||
/// type (json or text) will be auto-detected. To specify a format, use | ||
/// --input-format. | ||
input: Option<String>, | ||
|
||
/// The input format. | ||
/// | ||
/// If not provided, the format will be auto-detected from the input. | ||
#[arg(short, long)] | ||
input_format: Option<InputFormat>, | ||
|
||
/// The output format. | ||
/// | ||
/// If not provided, the format will be the same as the input. | ||
#[arg(short, long)] | ||
output_format: Option<OutputFormat>, | ||
|
||
/// Validate the CQL2 | ||
#[arg(long, default_value_t = true, action = ArgAction::Set)] | ||
validate: bool, | ||
|
||
/// Verbosity. | ||
/// | ||
/// Provide this argument several times to turn up the chatter. | ||
#[arg(short, long, action = ArgAction::Count)] | ||
verbose: u8, | ||
} | ||
|
||
/// The input CQL2 format. | ||
#[derive(Debug, ValueEnum, Clone)] | ||
pub enum InputFormat { | ||
/// cql2-json | ||
Json, | ||
|
||
/// cql2-text | ||
Text, | ||
} | ||
|
||
/// The output CQL2 format. | ||
#[derive(Debug, ValueEnum, Clone)] | ||
enum OutputFormat { | ||
/// cql2-json, pretty-printed | ||
JsonPretty, | ||
|
||
/// cql2-json, compact | ||
Json, | ||
|
||
/// cql2-text | ||
Text, | ||
|
||
/// SQL | ||
Sql, | ||
} | ||
|
||
impl Cli { | ||
/// Runs the cli. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use cql2_cli::Cli; | ||
/// use clap::Parser; | ||
/// | ||
/// let cli = Cli::try_parse_from(&["cql2", "landsat:scene_id = 'LC82030282019133LGN00'"]).unwrap(); | ||
/// cli.run(); | ||
/// ``` | ||
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,121 +1,6 @@ | ||
use clap::{ArgAction, Parser, ValueEnum}; | ||
use cql2::{Expr, Validator}; | ||
use std::io::Read; | ||
|
||
#[derive(Debug, Parser)] | ||
struct Cli { | ||
/// The input CQL2 | ||
/// | ||
/// If not provided, or `-`, the CQL2 will be read from standard input. The | ||
/// type (json or text) will be auto-detected. To specify a format, use | ||
/// --input-format. | ||
input: Option<String>, | ||
|
||
/// The input format. | ||
/// | ||
/// If not provided, the format will be auto-detected from the input. | ||
#[arg(short, long)] | ||
input_format: Option<InputFormat>, | ||
|
||
/// The output format. | ||
/// | ||
/// If not provided, the format will be the same as the input. | ||
#[arg(short, long)] | ||
output_format: Option<OutputFormat>, | ||
|
||
/// Validate the CQL2 | ||
#[arg(long, default_value_t = true, action = ArgAction::Set)] | ||
validate: bool, | ||
|
||
/// Verbosity. | ||
/// | ||
/// Provide this argument several times to turn up the chatter. | ||
#[arg(short, long, action = ArgAction::Count)] | ||
verbose: u8, | ||
} | ||
|
||
#[derive(Debug, ValueEnum, Clone)] | ||
enum InputFormat { | ||
/// cql2-json | ||
Json, | ||
|
||
/// cql2-text | ||
Text, | ||
} | ||
|
||
#[derive(Debug, ValueEnum, Clone)] | ||
enum OutputFormat { | ||
/// cql2-json, pretty-printed | ||
JsonPretty, | ||
|
||
/// cql2-json, compact | ||
Json, | ||
|
||
/// cql2-text | ||
Text, | ||
|
||
/// SQL | ||
Sql, | ||
} | ||
use clap::Parser; | ||
use cql2_cli::Cli; | ||
|
||
fn main() { | ||
let cli = Cli::parse(); | ||
let input = cli | ||
.input | ||
.and_then(|input| if input == "-" { None } else { Some(input) }) | ||
.unwrap_or_else(read_stdin); | ||
let input_format = cli.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).unwrap(), | ||
InputFormat::Text => match cql2::parse_text(&input) { | ||
Ok(expr) => expr, | ||
Err(err) => { | ||
eprintln!("[ERROR] Parsing error: {input}"); | ||
eprintln!("{err}"); | ||
std::process::exit(1) | ||
} | ||
}, | ||
}; | ||
if cli.validate { | ||
let validator = Validator::new().unwrap(); | ||
let value = serde_json::to_value(&expr).unwrap(); | ||
if let Err(error) = validator.validate(&value) { | ||
eprintln!("[ERROR] Invalid CQL2: {input}"); | ||
match cli.verbose { | ||
0 => eprintln!("For more detailed validation information, use -v"), | ||
1 => eprintln!("For more detailed validation information, use -vv\n{error}"), | ||
2 => eprintln!("For more detailed validation information, use -vvv\n{error:#}"), | ||
_ => { | ||
let detailed_output = error.detailed_output(); | ||
eprintln!("{detailed_output:#}"); | ||
} | ||
} | ||
std::process::exit(1) | ||
} | ||
} | ||
let output_format = cli.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).unwrap(), | ||
OutputFormat::Json => serde_json::to_writer(std::io::stdout(), &expr).unwrap(), | ||
OutputFormat::Text => print!("{}", expr.to_text().unwrap()), | ||
OutputFormat::Sql => { | ||
serde_json::to_writer_pretty(std::io::stdout(), &expr.to_sql().unwrap()).unwrap() | ||
} | ||
} | ||
println!() | ||
} | ||
|
||
fn read_stdin() -> String { | ||
let mut buf = String::new(); | ||
std::io::stdin().read_to_string(&mut buf).unwrap(); | ||
buf | ||
Cli::parse().run() | ||
} |
Oops, something went wrong.