transip 0.4.2

Call functions on the Transip Api Endpoint
Documentation
use std::{fmt::Display, str::FromStr};

use crate::Error;

#[allow(dead_code)]
#[derive(PartialEq, Debug, Clone)]
pub enum TokenExpiration {
    Seconds(u8),
    Minutes(u8),
    Hours(u8),
}

impl Default for TokenExpiration {
    fn default() -> Self {
        Self::Minutes(5)
    }
}

fn fmt(count: &u8, unit: &'static str) -> String {
    format!("{count} {unit}") + if count <= &1 { "" } else { "s" }
}

impl Display for TokenExpiration {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                TokenExpiration::Seconds(seconds) => fmt(seconds, "second"),
                TokenExpiration::Minutes(minutes) => fmt(minutes, "minute"),
                TokenExpiration::Hours(hours) => fmt(hours, "hour"),
            }
        )
    }
}

impl FromStr for TokenExpiration {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let splitted = s.split(' ').collect::<Vec<_>>();
        if splitted.len() != 2 {
            return Err(Error::ParseExpiration("String should contain 2 words"));
        };
        let first_word = splitted[0].trim().parse::<u8>()?;
        let second_word = splitted[1].trim();

        const IS_SECONDS: [&str; 2] = ["second", "seconds"];
        if IS_SECONDS.contains(&second_word.to_lowercase().as_str()) {
            return Ok(TokenExpiration::Seconds(first_word));
        }

        const IS_MINUTES: [&str; 2] = ["minute", "minutes"];
        if IS_MINUTES.contains(&second_word.to_lowercase().as_str()) {
            return Ok(TokenExpiration::Minutes(first_word));
        }

        const IS_HOURS: [&str; 2] = ["hour", "hours"];
        if IS_HOURS.contains(&second_word.to_lowercase().as_str()) {
            return Ok(TokenExpiration::Hours(first_word));
        }

        Err(Error::ParseExpiration("Invalid"))
    }
}

#[cfg(test)]
mod test {
    use super::TokenExpiration;

    #[test]
    fn seconds_ok() {
        let s = "5 seconds";
        let expiration = s.parse::<TokenExpiration>().unwrap();
        assert_eq!(expiration, TokenExpiration::Seconds(5));
    }

    #[test]
    fn seconds_error() {
        assert!("10 secconds".parse::<TokenExpiration>().is_err());
        assert!("five seconds".parse::<TokenExpiration>().is_err());
    }

    #[test]
    fn minutes_ok() {
        assert_eq!(
            "10 minutes".parse::<TokenExpiration>().unwrap(),
            TokenExpiration::Minutes(10),
        );
    }

    #[test]
    fn minutes_err() {
        assert!("10 minuttes".parse::<TokenExpiration>().is_err());
        assert!("ten minutes".parse::<TokenExpiration>().is_err());
    }

    #[test]
    fn hours_ok() {
        assert_eq!(
            "60 hours".parse::<TokenExpiration>().unwrap(),
            TokenExpiration::Hours(60),
        );
        assert_eq!(
            "60 HOUR".parse::<TokenExpiration>().unwrap(),
            TokenExpiration::Hours(60),
        );
    }

    #[test]
    fn hours_err() {
        assert!("10 uur".parse::<TokenExpiration>().is_err());
        assert!("ten hours".parse::<TokenExpiration>().is_err());
    }
}