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

const PARSING_ERROR: &str = "Invalid time. Use format [hh:mm].";
const SECONDS_PER_DAY: u32 = 24 * 60 * 60; 

pub struct Time {
    pub hours: u8,
    pub minutes: u8,
}

impl Time {
    pub fn from(time: &str) -> Result<Time, String> {
        let parts: Vec<&str> = time.split(":").collect();

        if parts.len() != 2 {
            return Err(String::from(PARSING_ERROR))
        }

        let hours = parse_u8(parts[0])?;
        let minutes = parse_u8(parts[1])?;

        let bounds_hours = hours < 24;
        let bounds_minutes = minutes < 60;

        if !bounds_hours || !bounds_minutes {
            return Err(String::from(PARSING_ERROR))
        }

        let time = Time {
            hours,
            minutes,
        };

        Ok(time)
    }

    pub fn current() -> Time {
        let local: DateTime<Local> = Local::now();
        Time {hours: local.hour() as u8, minutes: local.minute() as u8}
    }

    pub fn secs(&self) -> u32 {
        (self.hours as u32 * 60 + self.minutes as u32) * 60 
    }

    pub fn percent(&self) -> f32 {
        self.secs() as f32 / SECONDS_PER_DAY as f32
    }

    pub fn percent_str(&self) -> String {
        let percent = self.percent() * 100.0;
        format!("{:.2}%", percent)
    }
}

fn parse_u8(num: &str) -> Result<u8, String> {
    if let Ok(num) = num.parse::<u8>() {
        Ok(num)
    } else {
        Err(String::from(PARSING_ERROR))
    }
}