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
use chrono::NaiveDate;

/// Required date format.
const DATE_FORMAT: &str = "%Y-%m-%d";

/// Validate line range argument value(s). Return vector of at most integers or error.
/// A valid line range can be either one line number (1 for line one) or two line numbers to indicate
/// multiple lines (0 20 to get first 20 lines).
pub fn valid_line_range_value(value: &str) -> Result<usize, String> {
    // Attempt to parse u32 values from string values.
    let res: usize = value
        .parse()
        .map_err(|_| format!("{} must be a valid usize.", value))?;

    Ok(res)
}

/// Validate that a string is date-like.
// NOTE: Currently strings are utilized internally. Evenaully `chrono` datetimes could be used, but
//       for now we only use `chrono` for validation purposes.
// TODO: Operate with `chrono` dates.
fn is_date_like(value: &str) -> Result<bool, String> {
    // Attempt to parse string as date.
    let res = NaiveDate::parse_from_str(value, DATE_FORMAT);

    // If we fail to parse `NaiveDate` correctly return false.
    if res.is_err() {
        return Ok(false);
    }

    // Otherwise correctly return true.
    Ok(true)
}

/// Validate date range argument value(s). Return vector of at most 2 strings or error.
/// A valid date range can be either one date string ("2022-01-01" for just January 1st) or two
/// strings to indicate an inclusive range of dates ("2022-01-01" "2022-01-02").
// TODO: Accept datetimes.
pub fn valid_date_range_value(value: &str) -> Result<String, String> {
    // Check if value passed is a valid date formatted string.
    if let Ok(false) = is_date_like(value) {
        return Err(format!("Date format must be {}.", DATE_FORMAT));
    }

    Ok(value.to_string())
}

#[test]
fn test_line_range_validation() {
    let valid_value = "0";
    let invalid_value = "foo";

    assert_eq!(valid_line_range_value(valid_value), Ok(0));
    assert_eq!(
        valid_line_range_value(invalid_value),
        Err(format!("{} must be a valid usize.", invalid_value))
    );
}

#[test]
fn test_date_range_validation() {
    let valid_value = "2022-01-01";
    let invalid_value = "foo";

    assert_eq!(
        valid_date_range_value(valid_value),
        Ok("2022-01-01".to_string())
    );
    assert_eq!(
        valid_date_range_value(invalid_value),
        Err(format!("Date format must be {}.", DATE_FORMAT))
    );
}