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