1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use clap::{Arg, AppSettings};
use std::path::PathBuf;
use std::str::FromStr;
use regex::Regex;
use term_size;
use std::fs;
lazy_static! {
static ref SIZE_ARG_RGX: Regex = Regex::new(r"(\d+)[xX](\d+)").unwrap();
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum AnsiOutputFormat {
Truecolor,
SimpleBlack,
SimpleWhite,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Options {
pub image: (String, PathBuf),
pub size: (u32, u32),
pub preserve_aspect: bool,
pub ansi_out: Option<AnsiOutputFormat>,
}
impl Options {
pub fn parse() -> Options {
let szarg_def;
let mut szarg = Arg::from_usage("-s --size [size] 'Output image resolution'").validator(Options::size_validator);
let have_dimms = if let Some((w, h)) = term_size::dimensions() {
szarg_def = format!("{}x{}", w, h - 1);
szarg = szarg.default_value(&szarg_def);
true
} else {
szarg = szarg.required(true);
false
};
let matches = app_from_crate!("\n")
.setting(AppSettings::ColoredHelp)
.arg(Arg::from_usage("<IMAGE> 'Image file to display'").validator(Options::image_file_validator))
.arg(szarg)
.arg(Arg::from_usage("-f --force 'Don\\'t preserve the image\\'s aspect ratio'"))
.arg(Arg::from_usage("-a --ansi [ANSI] 'Force output ANSI escapes'").possible_values(&["truecolor", "simple-black", "simple-white"]))
.get_matches();
let image = matches.value_of("IMAGE").unwrap();
Options {
image: (image.to_string(), fs::canonicalize(image).unwrap()),
size: Options::parse_size(matches.value_of("size").unwrap()).unwrap(),
preserve_aspect: !matches.is_present("force"),
ansi_out: if cfg!(not(target_os = "windows")) || !have_dimms || matches.is_present("ansi") {
match matches.value_of("ansi").unwrap_or("truecolor") {
"truecolor" => Some(AnsiOutputFormat::Truecolor),
"simple-black" => Some(AnsiOutputFormat::SimpleBlack),
"simple-white" => Some(AnsiOutputFormat::SimpleWhite),
_ => unreachable!(),
}
} else {
None
},
}
}
fn parse_size(s: &str) -> Option<(u32, u32)> {
SIZE_ARG_RGX.captures(s).map(|c| (u32::from_str(c.get(1).unwrap().as_str()).unwrap(), u32::from_str(c.get(2).unwrap().as_str()).unwrap()))
}
fn image_file_validator(s: String) -> Result<(), String> {
fs::canonicalize(&s).map(|_| ()).map_err(|_| format!("Image file \"{}\" not found", s))
}
fn size_validator(s: String) -> Result<(), String> {
match Options::parse_size(&s) {
None => Err(format!("\"{}\" is not a valid size (in format \"NNNxMMM\")", s)),
Some((0, _)) | Some((_, 0)) => Err(format!("Can't resize image to size 0")),
Some(_) => Ok(()),
}
}
}