use std::{error::Error, fs::File, io::Write, process};
use clap::{ArgGroup, Clap, crate_version};
use colored::Colorize;
use ptero::{
cli::{
capacity::GetCapacityCommand, decoder::DecodeSubCommand,
encoder::EncodeSubCommand, writer::Writer,
},
log::{get_file_logger, get_stdout_logger, verbosity_to_level_filter},
};
use serde_json::json;
const BANNER: &str = r#"
______ ______ ______ ______ ______
/\ == \ /\__ _\ /\ ___\ /\ == \ /\ __ \
\ \ _-/ \/_/\ \/ \ \ __\ \ \ __< \ \ \/\ \
\ \_\ \ \_\ \ \_____\ \ \_\ \_\ \ \_____\
\/_/ \/_/ \/_____/ \/_/ /_/ \/_____/
"#;
const APP_NAME: &str = "Ptero CLI";
#[derive(Clap)]
#[clap(
version = crate_version!(),
author = "Paweł G. <dev.baymax42@gmail.com>",
name = format ! ("{}{}", BANNER, APP_NAME),
group = ArgGroup::new("output_args").required(false),
)]
struct Opts {
#[clap(subcommand)]
subcommand: SubCommand,
#[clap(short, long, group = "output_args")]
output: Option<String>,
#[clap(short, parse(from_occurrences))]
verbose: u8,
#[clap(long, group = "output_args")]
json: bool,
#[clap(long)]
log_file: Option<String>,
}
#[derive(Clap)]
enum SubCommand {
#[clap(name = "encode", group = ArgGroup::new("method_args").required(true))]
Encode(EncodeSubCommand),
#[clap(name = "decode", group = ArgGroup::new("method_args").required(true))]
Decode(DecodeSubCommand),
#[clap(name = "capacity", group = ArgGroup::new("method_args").required(true))]
GetCapacity(GetCapacityCommand),
}
#[cfg(not(tarpaulin_include))]
fn enable_logging(
verbose: u8,
log_path: Option<String>,
) -> std::result::Result<(), Box<dyn Error>> {
let level_filter = verbosity_to_level_filter(verbose);
let mut log_builder = fern::Dispatch::new()
.level(level_filter)
.chain(get_stdout_logger());
log_builder = if let Some(path) = log_path {
log_builder.chain(get_file_logger(&path))
} else {
log_builder
};
log_builder.apply()?;
Ok(())
}
#[cfg(not(tarpaulin_include))]
fn run_subcommand(subcommand: SubCommand) -> Result<Vec<u8>, Box<dyn Error>> {
let result = match subcommand {
SubCommand::Encode(command) => command.run()?,
SubCommand::Decode(command) => command.run()?,
SubCommand::GetCapacity(command) => {
let capacity: u32 = command.run()?;
let output_str = format!("{} b", capacity);
output_str.as_bytes().into()
}
};
Ok(result)
}
#[cfg(not(tarpaulin_include))]
fn main() -> Result<(), Box<dyn Error>> {
let opts: Opts = Opts::parse();
enable_logging(opts.verbose, opts.log_file)?;
Writer::print(&BANNER.purple().bold().to_string());
let result = run_subcommand(opts.subcommand);
if let Err(error) = &result {
let error_message = format!("{}", error);
Writer::error(&error_message);
process::exit(1);
} else {
let cli_output = &result?;
if let Some(path) = &opts.output {
let mut output_file = File::create(path)?;
output_file.write_all(&cli_output)?;
Writer::info(&format!("Saved to '{}'", &path));
} else {
let output = &String::from_utf8_lossy(&cli_output);
if opts.json {
println!(
"{}",
json!({
"type": "success",
"result": output,
})
);
} else {
println!("{}", output);
}
}
}
Ok(())
}