use clap::builder::{OsStr, Str};
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, num::ParseIntError, str::FromStr};
pub const ZERO_PERCENT: Percentage = Percentage { value: 0 };
pub const FIFTY_PERCENT: Percentage = Percentage { value: 50 };
pub const ONE_HUNDRED_PERCENT: Percentage = Percentage { value: 100 };
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Percentage {
value: u8,
}
impl Percentage {
pub fn expect_from(value: u8) -> Percentage {
match Percentage::try_from(value) {
Err(e) => panic!("{}", e),
Ok(p) => p,
}
}
pub fn apply_to(&self, value: u8) -> u8 {
((value as u16 * self.value as u16) / 100u16) as u8
}
pub fn value(&self) -> u8 {
self.value
}
}
impl TryFrom<u8> for Percentage {
type Error = PercentageParserError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
if value > 100 {
Err(PercentageParserError::InputTooBig(value))
} else {
Ok(Percentage { value })
}
}
}
#[derive(thiserror::Error, Debug)]
pub enum PercentageParserError {
#[error("Input value {0} cannot be greater than 100")]
InputTooBig(u8),
#[error("Malformed string input for u8")]
MalformedString(#[from] ParseIntError),
}
impl FromStr for Percentage {
type Err = PercentageParserError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Percentage::try_from(u8::from_str(s)?)
}
}
impl From<Percentage> for OsStr {
fn from(percentage: Percentage) -> OsStr {
OsStr::from(Str::from(percentage.value.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::test_utils::assert_err;
#[test]
#[should_panic]
fn from_should_panic_if_value_is_over_100() {
Percentage::expect_from(101);
}
#[test]
fn from_should_give_err_if_value_is_over_100() {
let res = Percentage::try_from(101);
assert_err!(res, Err(PercentageParserError::InputTooBig(101)));
}
#[test]
fn from_str_happy_case() {
Percentage::from_str("50").unwrap();
}
#[test]
#[should_panic]
fn from_str_should_panic_if_value_is_over_100() {
Percentage::from_str("101").unwrap();
}
#[test]
#[should_panic]
fn from_str_should_panic_if_value_is_not_u8() {
Percentage::from_str("bleh").unwrap();
}
#[test]
fn try_from_happy_case() {
let test = 15;
assert_eq!(test, Percentage::expect_from(15).value);
}
#[test]
fn from_happy_case() {
let test = 15;
assert_eq!(test, Percentage::expect_from(15).value);
}
}