rodalies_cli/config/
cli.rs1use chrono::{Datelike, Local};
2use clap::{
3 crate_authors, crate_description, crate_name, crate_version, value_parser, Arg, ArgAction,
4 ArgMatches, Command,
5};
6use prettytable::{format, Table};
7use std::error::Error;
8
9pub fn init_cli() -> ArgMatches {
11 let cli = Command::new(crate_name!())
12 .about(crate_description!())
13 .version(crate_version!())
14 .author(crate_authors!())
15 .arg(
16 Arg::new("interactive")
17 .required(false)
18 .short('i')
19 .long("interactive")
20 .action(ArgAction::SetTrue)
21 .value_parser(value_parser!(bool))
22 .default_missing_value("true")
23 .help("Enable interactive train timetable search. No value required.")
24 )
25 .arg(
26 Arg::new("search")
27 .required(false)
28 .short('s')
29 .long("search")
30 .env("RODALIES_CLI_SEARCH")
31 .action(ArgAction::Set)
32 .help("Search the ID of a given station's name pattern, to later use it on your origin or destination.")
33 )
34 .arg(
35 Arg::new("from")
36 .required(false)
37 .short('f')
38 .long("from")
39 .env("RODALIES_CLI_FROM")
40 .action(ArgAction::Set)
41 .help("The origin's station ID.")
42 )
43 .arg(
44 Arg::new("to")
45 .required(false)
46 .short('t')
47 .long("to")
48 .env("RODALIES_CLI_TO")
49 .action(ArgAction::Set)
50 .help("The destinations's station ID.")
51 )
52 .arg(
53 Arg::new("day")
54 .required(false)
55 .short('d')
56 .long("day")
57 .action(ArgAction::Set)
58 .help("The day value of the date to search for (default = today's day).")
59 )
60 .arg(
61 Arg::new("month")
62 .required(false)
63 .short('m')
64 .long("month")
65 .action(ArgAction::Set)
66 .help("The month value of the date to search for (default = today's month).")
67 )
68 .arg(
69 Arg::new("year")
70 .required(false)
71 .short('y')
72 .long("year")
73 .action(ArgAction::Set)
74 .help("The year value of the date to search for (default = today's year).")
75 );
76
77 cli.get_matches()
78}
79
80pub fn init_results_table() -> Table {
82 let mut results_table = Table::new();
83 results_table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
84 results_table
85}
86
87pub fn interactive_mode(args: &ArgMatches) -> Result<bool, Box<dyn Error>> {
89 let from = args.contains_id("from");
90 let to = args.contains_id("to");
91 let search = args.contains_id("search");
92
93 let is_interactive = !(from || to || search);
94 println!("✨ Interactive mode enabled: '{}'", is_interactive);
95 Ok(is_interactive)
96}
97
98pub fn parse_search(args: &ArgMatches) -> Result<String, Box<dyn Error>> {
100 let search = args.get_one::<String>("search").unwrap();
101 println!("🔍 Searching stations that contain the text: '{}'", search);
102 Ok(search.to_string())
103}
104
105pub fn parse_trip(args: &ArgMatches) -> Result<(String, String), Box<dyn Error>> {
107 let from = args.get_one::<String>("from");
108 let to = args.get_one::<String>("to");
109
110 if from.is_none() || to.is_none() {
111 return Err("🚨 Please, specify origin and destination station IDs".into());
112 }
113
114 Ok((from.unwrap().to_string(), to.unwrap().to_string()))
115}
116
117pub fn parse_date(args: &ArgMatches) -> Result<String, Box<dyn Error>> {
119 let dt = Local::now();
120 let day = match args.get_one::<String>("day") {
121 Some(day) => match day.parse::<u32>() {
122 Ok(day) => day,
123 _ => return Err("🚨 Please, specify right value for day".into()),
124 },
125 None => dt.day(),
126 };
127 let month = match args.get_one::<String>("month") {
128 Some(month) => match month.parse::<u32>() {
129 Ok(month) => month,
130 _ => return Err("🚨 Please, specify right value for month".into()),
131 },
132 None => dt.month(),
133 };
134 let year = match args.get_one::<String>("year") {
135 Some(year) => match year.parse::<i32>() {
136 Ok(year) => year,
137 _ => return Err("🚨 Please, specify right value for year".into()),
138 },
139 None => dt.year(),
140 };
141
142 println!(
143 "🔍 Searching timetable for date {:02}/{:02}/{}",
144 day, month, year
145 );
146
147 Ok(format!("{:02}/{:02}/{}", day, month, year))
148}
149
150#[cfg(test)]
151mod tests {
152 use super::{init_cli, init_results_table};
153
154 #[test]
155 fn test_init_results_table_is_empty() {
156 let results_table = init_results_table();
157 assert!(results_table.is_empty());
158 }
159
160 #[test]
161 fn test_init_cli_with_defaults() {
162 let args = init_cli();
163 assert_eq!(args.ids().len(), 1);
164 assert_eq!(
165 args.ids().map(|id| id.as_str()).collect::<Vec<_>>(),
166 ["interactive"]
167 );
168 }
169}