use std::{env, num::NonZeroU8, process};
use aarty::{COLORS, REVERSE};
use image::imageops::FilterType;
const VERSION: &str = match option_env!("AARTY_BUILD_OVERWRITE_VERSION") {
Some(v) => v,
None => env!("CARGO_PKG_VERSION"),
};
pub struct Opts {
pub path: Option<String>,
pub sym_set: Vec<char>,
pub scale: NonZeroU8,
pub width: Option<u32>,
pub height: Option<u32>,
pub background: Option<String>,
pub flags: u8,
pub sf: FilterType,
}
impl Opts {
pub fn from_args() -> Result<Opts, String> {
let mut args = env::args().skip(1);
let mut opts = Opts::default();
macro_rules! err {
($arg:ident) => {
format!("Expected one argument after {}, found 0.", $arg)
};
(parse; $item:expr, $error:ident) => {
format!("Can't parse the provided {}, because `{}`", $item, $error)
};
}
macro_rules! value {
($arg:ident) => {
args.next().ok_or(err!($arg))
};
(parse; $name:expr, $arg:ident) => {
args
.next()
.ok_or(err!($arg))?
.parse()
.map_err(|e| err!(parse; $name, e))
}
}
while let Some(arg) = args.next() {
if !arg.starts_with('-') {
opts.path = Some(arg);
continue;
}
let arg = arg.to_lowercase();
let arg = arg.trim_start_matches('-');
match arg {
"c" | "sympols" | "chars" => opts.sym_set = value!(arg)?.chars().collect(),
"s" | "scale" => {
opts.scale = NonZeroU8::new(value!(parse; "scale", arg)?)
.ok_or_else(|| "The scale shouldn't be less than 1,".to_string())?
}
"w" | "col" | "columans" | "width" => {
opts.width = Some(value!(parse; "width", arg)?)
}
"h" | "row" | "rows" | "height" => {
opts.height = Some(value!(parse; "height", arg)?)
}
"b" | "back" | "background" => opts.background = Some(value!(arg)?),
"r" | "reverse" => opts.flags |= REVERSE,
"u" | "color" | "colors" => opts.flags |= COLORS,
"sft" | "st" => opts.sf = FilterType::Triangle,
"sfc" | "sc" => opts.sf = FilterType::CatmullRom,
"sfg" | "sg" => opts.sf = FilterType::Gaussian,
"sfl" | "sl" => opts.sf = FilterType::Lanczos3,
"sfn" | "sn" => opts.sf = FilterType::Nearest,
"v" | "version" => info(format!("aarty v{VERSION}")),
"help" => print_help(),
unknown => return Err(format!("Unknown option {unknown}")),
}
}
Ok(opts)
}
}
impl Default for Opts {
fn default() -> Self {
Opts {
path: None,
sym_set: vec![
' ', '.', ',', '-', '~', '!', ';', ':', '=', '*', '&', '%', '$', '@', '#',
],
scale: unsafe { NonZeroU8::new_unchecked(4) },
width: None,
height: None,
background: None,
flags: 0,
sf: FilterType::Nearest,
}
}
}
#[cold]
fn info(msg: String) -> ! {
println!("{msg}");
process::exit(0)
}
#[cold]
fn print_help() -> ! {
let defaults = Opts::default();
info(format!(
"Usage: aarty [options] [path]
Options:
-c, --symbols, --chars <chars> Symbols set to use (default: \"{}\")
-s, --scale <n> Scale factor (>= 1, default: {})
-w, --col, --columns, --width <n> Output width (default: auto)
-h, --row, --rows, --height <n> Output height (default: auto)
-b, --back, --background <color> Background color (default: none)
-r, --reverse Reverse output
-u, --color, --colors Enable colors
--sft, --st Triangle filter
--sfc, --sc Catmull-Rom filter
--sfg, --sg Gaussian filter
--sfl, --sl Lanczos3 filter
--sfn, --sn Nearest filter (default)
-v, --version Show version
--help Show this help",
defaults.sym_set.iter().collect::<String>(),
defaults.scale.get(),
))
}