1use std::path::PathBuf;
2
3use chrono::FixedOffset;
4use clap::{builder::TypedValueParser, Parser, ValueEnum, error::ErrorKind};
5use grep::{
6 matcher::{Captures, Matcher},
7 regex::RegexMatcher,
8};
9
10#[derive(Parser, Debug)]
12#[command(author, version, about, long_about = None)]
13pub struct Args {
14 #[arg(short = 'n', long = "no-ignore")]
16 pub no_ignore: bool,
17
18 #[arg(short = 'e', long = "no-error")]
20 pub no_error: bool,
21
22 #[arg(
28 value_enum,
29 rename_all = "kebab_case",
30 short = 'd',
31 long = "display-mode",
32 default_value = "default"
33 )]
34 pub display_mode: DisplayMode,
35
36 #[arg(value_parser, default_value = "./")]
38 pub root_directory: PathBuf,
39
40 #[arg(short = 'p', long = "pattern", value_parser, default_value = "*")]
42 pub ignore_pattern: String,
43 #[arg(short = 't', long = "timezone-offset", value_parser = FixedOffsetParser, default_value = "+00:00", allow_hyphen_values = true)]
45 pub timezone_offset: FixedOffset,
46}
47
48#[derive(ValueEnum, Debug, Clone)]
49pub enum DisplayMode {
50 Concise,
51 OverdueOnly,
52 Default,
53 }
55
56#[derive(Clone)]
57struct FixedOffsetParser;
58
59impl TypedValueParser for FixedOffsetParser {
60 type Value = FixedOffset;
61
62 fn parse_ref(
63 &self,
64 _cmd: &clap::Command,
65 _arg: Option<&clap::Arg>,
66 value: &std::ffi::OsStr,
67 ) -> Result<FixedOffset, clap::Error> {
68 let offset_string = value.to_str().expect("Should be string!");
69 const OFFSET_PATTERN: &str = r"(-|\+)(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])";
70
71 let matcher = RegexMatcher::new(OFFSET_PATTERN).expect("Regex should be valid");
72 let mut captures = matcher.new_captures().expect("Regex should be valid");
73 matcher
74 .captures(offset_string.as_bytes(), &mut captures)
75 .expect("Regex should be valid");
76
77 if matcher.capture_count() != 4 {
79 Err(clap::Error::raw(
80 ErrorKind::ValueValidation,
81 "UTC offset does not follow the format [+|-][HH]:[SS]",
82 ))
83 } else {
84 let offset_seconds: i32 = (3600
86 * offset_string[captures.get(2).unwrap()]
87 .parse::<i32>()
88 .unwrap())
89 + (60
90 * offset_string[captures.get(3).unwrap()]
91 .parse::<i32>()
92 .unwrap());
93
94 if &offset_string[captures.get(1).unwrap()] == "+" {
95 Ok(FixedOffset::east(offset_seconds))
96 } else {
97 Ok(FixedOffset::west(offset_seconds))
98 }
99 }
100 }
101}