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
110
111
112
113
114
115
116
use self::super::ops::IncludeDirectory;
use clap::{AppSettings, Arg};
use std::path::PathBuf;
use std::iter;
use std::fs;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Options {
pub source_file: Option<(String, PathBuf)>,
pub include_directories: Vec<IncludeDirectory>,
pub output_file: Option<(String, PathBuf)>,
pub verbose: bool,
pub separator: String,
pub free_date: bool,
}
impl Options {
pub fn parse() -> Options {
let matches = app_from_crate!("\n")
.setting(AppSettings::ColoredHelp)
.arg(Arg::from_usage("<SOURCE> 'File to assemble ePub from'").validator(Options::source_file_validator))
.arg(Arg::from_usage("<TARGET> 'File to write'"))
.arg(Arg::from_usage("-v --verbose 'Print more information'"))
.arg(Arg::from_usage("-D --free-date 'Parse more datetime formats'"))
.arg(Arg::from_usage("-S --separator [SEPARATOR] 'Custom separator'").default_value(":").validator(Options::separator_validator).required(false))
.arg(Arg::from_usage("-I --include [INC_DIR]... 'Additional include directory. Format: [name=]path'")
.validator(Options::include_dir_validator)
.required(false))
.get_matches();
let source = Options::optional_fname_arg(matches.value_of("SOURCE").unwrap());
let target = Options::optional_fname_arg(matches.value_of("TARGET").unwrap());
let source_root = match source.and_then(|src| src.rfind('/').or_else(|| src.rfind('\\'))) {
Some(s) => IncludeDirectory::Unnamed { dir: (source.unwrap()[..s + 1].to_string(), PathBuf::from(&source.unwrap()[..s])) },
None => IncludeDirectory::Unnamed { dir: ("".to_string(), PathBuf::from(".")) },
};
Options {
source_file: source.map(|s| (s.to_string(), PathBuf::from(s))),
include_directories: iter::once(source_root)
.chain(matches.values_of("include").into_iter().flat_map(|v| v.map(str::parse).map(Result::unwrap)))
.collect(),
output_file: target.map(|tgt| (tgt.to_string(), PathBuf::from(tgt))),
verbose: matches.is_present("verbose"),
separator: matches.value_of("separator").unwrap_or(":").to_string(),
free_date: matches.is_present("free-date"),
}
}
fn source_file_validator(s: String) -> Result<(), String> {
if s == "-" {
Ok(())
} else {
fs::canonicalize(&s).map_err(|_| format!("Source file \"{}\" not found", s)).and_then(|f| if f.is_file() {
Ok(())
} else {
Err(format!("Source file \"{}\" not actualy a file", s))
})
}
}
fn separator_validator(s: String) -> Result<(), String> {
if s.is_empty() {
Err("Separator empty".to_string())
} else {
Ok(())
}
}
fn include_dir_validator(s: String) -> Result<(), String> {
s.parse::<IncludeDirectory>().map(|_| ()).map_err(|e| {
let mut out = Vec::new();
e.print_error(&mut out);
String::from_utf8(out).unwrap()
})
}
fn optional_fname_arg(s: &str) -> Option<&str> {
if s == "-" { None } else { Some(s) }
}
}