jobber3 0.1.0

Command line tool for tracking work time
use regex::Regex;

#[derive(PartialEq, Debug)]
pub enum PartialDateTime {
    HM(u32, u32),
    YMDHM(i32, u32, u32, u32, u32),
    MDHM(u32, u32, u32, u32),
    RHM(i32, u32, u32),
}

pub fn parse_partial_date_time(dt: &str) -> Option<PartialDateTime> {
    parse_hm(&dt).or(parse_dmyhm(&dt).or(parse_mdyhm(dt)
        .or(parse_ymdhm(&dt).or(parse_dmhm(&dt).or(parse_mdhm(dt).or(parse_rhm(&dt)))))))
}

// let base_dt: DateTime<Local> = DateTime::from(base_dt);
// let dt = Local
//     .with_ymd_and_hms(
//         base_dt.year(),
//         cap[2].parse::<u32>().unwrap(),
//         cap[1].parse::<u32>().unwrap(),
//         cap[3].parse::<u32>().unwrap(),
//         cap[4].parse::<u32>().unwrap(),
//         0,
//     )
//     .unwrap();
// let dt = DateTime::with_timezone(&dt, &Utc);
// println!("incomplete date & time: {:?}", &dt);
// return Some(dt);

/// parse time "HH:MM"
fn parse_hm(dt: &str) -> Option<PartialDateTime> {
    let re = Regex::new(r"^(\d{1,2}):(\d{1,2})$").unwrap();
    for cap in re.captures_iter(dt) {
        return Some(PartialDateTime::HM(
            cap[1].parse::<u32>().unwrap(),
            cap[2].parse::<u32>().unwrap(),
        ));
    }
    None
}

#[test]
fn test_parse_hm() {
    assert_eq!(parse_hm("01:00"), Some(PartialDateTime::HM(1, 0)));
    assert_eq!(parse_hm("1:0"), Some(PartialDateTime::HM(1, 0)));
}

/// parse german date and time "dd.mm.yyyy,HH:MM"
fn parse_dmyhm(dt: &str) -> Option<PartialDateTime> {
    let re = Regex::new(r"^(\d{1,2}).(\d{1,2}).(\d{4}),(\d{1,2}):(\d{1,2})$").unwrap();
    for cap in re.captures_iter(dt) {
        return Some(PartialDateTime::YMDHM(
            cap[3].parse::<i32>().unwrap(),
            cap[2].parse::<u32>().unwrap(),
            cap[1].parse::<u32>().unwrap(),
            cap[4].parse::<u32>().unwrap(),
            cap[5].parse::<u32>().unwrap(),
        ));
    }
    None
}

#[test]
fn test_parse_dmyhm() {
    assert_eq!(
        parse_dmyhm("01.02.2023,01:00"),
        Some(PartialDateTime::YMDHM(2023, 2, 1, 1, 0))
    );
    assert_eq!(
        parse_dmyhm("1.2.2023,1:0"),
        Some(PartialDateTime::YMDHM(2023, 2, 1, 1, 0))
    );
}

/// parse english date and time "mm/dd/yyyy,HH:MM"
fn parse_mdyhm(dt: &str) -> Option<PartialDateTime> {
    let re = Regex::new(r"^(\d{1,2})/(\d{1,2})/(\d{4}),(\d{1,2}):(\d{1,2})$").unwrap();
    for cap in re.captures_iter(dt) {
        return Some(PartialDateTime::YMDHM(
            cap[3].parse::<i32>().unwrap(),
            cap[1].parse::<u32>().unwrap(),
            cap[2].parse::<u32>().unwrap(),
            cap[4].parse::<u32>().unwrap(),
            cap[5].parse::<u32>().unwrap(),
        ));
    }
    None
}

#[test]
fn test_parse_mdyhm() {
    assert_eq!(
        parse_mdyhm("02/01/2023,01:00"),
        Some(PartialDateTime::YMDHM(2023, 2, 1, 1, 0))
    );
    assert_eq!(
        parse_mdyhm("2/1/2023,1:0"),
        Some(PartialDateTime::YMDHM(2023, 2, 1, 1, 0))
    );
}

/// parse date and time "yyyy-mm-dd,HH:MM"
fn parse_ymdhm(dt: &str) -> Option<PartialDateTime> {
    let re = Regex::new(r"^(\d{4})-(\d{1,2})-(\d{1,2}),(\d{1,2}):(\d{1,2})$").unwrap();
    for cap in re.captures_iter(dt) {
        return Some(PartialDateTime::YMDHM(
            cap[1].parse::<i32>().unwrap(),
            cap[2].parse::<u32>().unwrap(),
            cap[3].parse::<u32>().unwrap(),
            cap[4].parse::<u32>().unwrap(),
            cap[5].parse::<u32>().unwrap(),
        ));
    }
    None
}

#[test]
fn test_parse_ymdhm() {
    assert_eq!(
        parse_ymdhm("2023-02-01,01:00"),
        Some(PartialDateTime::YMDHM(2023, 2, 1, 1, 0))
    );
    assert_eq!(
        parse_ymdhm("2023-2-1,1:0"),
        Some(PartialDateTime::YMDHM(2023, 2, 1, 1, 0))
    );
}

/// parse german date without year and time "dd.mm.,HH:MM"
fn parse_dmhm(dt: &str) -> Option<PartialDateTime> {
    let re = Regex::new(r"^(\d{1,2}).(\d{1,2}).,(\d{1,2}):(\d{1,2})$").unwrap();
    for cap in re.captures_iter(dt) {
        return Some(PartialDateTime::MDHM(
            cap[2].parse::<u32>().unwrap(),
            cap[1].parse::<u32>().unwrap(),
            cap[3].parse::<u32>().unwrap(),
            cap[4].parse::<u32>().unwrap(),
        ));
    }
    None
}

#[test]
fn test_parse_dmhm() {
    assert_eq!(
        parse_dmhm("01.02.,01:00"),
        Some(PartialDateTime::MDHM(2, 1, 1, 0))
    );
    assert_eq!(
        parse_dmhm("1.2.,1:0"),
        Some(PartialDateTime::MDHM(2, 1, 1, 0))
    );
}

/// parse english date without year and time "mm/dd,HH:MM"
fn parse_mdhm(dt: &str) -> Option<PartialDateTime> {
    let re = Regex::new(r"^(\d{1,2})/(\d{1,2}),(\d{1,2}):(\d{1,2})$").unwrap();
    for cap in re.captures_iter(dt) {
        return Some(PartialDateTime::MDHM(
            cap[1].parse::<u32>().unwrap(),
            cap[2].parse::<u32>().unwrap(),
            cap[3].parse::<u32>().unwrap(),
            cap[4].parse::<u32>().unwrap(),
        ));
    }
    None
}

#[test]
fn test_parse_mdhm() {
    assert_eq!(
        parse_mdhm("02/01,01:00"),
        Some(PartialDateTime::MDHM(2, 1, 1, 0))
    );
    assert_eq!(
        parse_mdhm("2/1,1:0"),
        Some(PartialDateTime::MDHM(2, 1, 1, 0))
    );
}

/// parse relative date and time "mm/dd,HH:MM"
fn parse_rhm(dt: &str) -> Option<PartialDateTime> {
    let re = Regex::new(r"^([\+-]\d+),(?P<H>\d{1,2}):(?P<M>\d{1,2})$").unwrap();
    for cap in re.captures_iter(dt) {
        return Some(PartialDateTime::RHM(
            cap[1].parse::<i32>().unwrap(),
            cap[2].parse::<u32>().unwrap(),
            cap[3].parse::<u32>().unwrap(),
        ));
    }
    None
}

#[test]
fn test_parse_rhm() {
    assert_eq!(parse_rhm("-01,01:00"), Some(PartialDateTime::RHM(-1, 1, 0)));
    assert_eq!(parse_rhm("-01,01:00"), Some(PartialDateTime::RHM(-1, 1, 0)));
    assert_eq!(parse_rhm("+1,1:0"), Some(PartialDateTime::RHM(1, 1, 0)));
    assert_eq!(parse_rhm("+1,1:0"), Some(PartialDateTime::RHM(1, 1, 0)));
}