diff --git a/fitnesstrax/app/src/types.rs b/fitnesstrax/app/src/types.rs index ac9a5da..4af25c1 100644 --- a/fitnesstrax/app/src/types.rs +++ b/fitnesstrax/app/src/types.rs @@ -100,3 +100,120 @@ impl From> for Weight { Self(value) } } + +#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] +pub struct Distance { + value: si::Meter, +} + +impl Distance { + pub fn format(&self, option: FormatOption) -> String { + match option { + FormatOption::Abbreviated => format!("{} km", self.value.value_unsafe / 1000.), + FormatOption::Full => format!("{} kilometers", self.value.value_unsafe / 1000.), + } + } + + pub fn parse(s: &str) -> Result { + let digits = take_digits(s.to_owned()); + let value = digits.parse::().map_err(|_| ParseError)?; + Ok(Distance { + value: value * 1000. * si::M, + }) + } +} + +impl std::ops::Add for Distance { + type Output = Distance; + fn add(self, rside: Self) -> Self::Output { + Self::Output::from(self.value + rside.value) + } +} + +impl std::ops::Sub for Distance { + type Output = Distance; + fn sub(self, rside: Self) -> Self::Output { + Self::Output::from(self.value - rside.value) + } +} + +impl std::ops::Deref for Distance { + type Target = si::Meter; + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl From> for Distance { + fn from(value: si::Meter) -> Self { + Self { value } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] +pub struct Duration { + value: si::Second, +} + +impl Duration { + pub fn format(&self, option: FormatOption) -> String { + let (hours, minutes) = self.hours_and_minutes(); + let (h, m) = match option { + FormatOption::Abbreviated => ("h", "m"), + FormatOption::Full => ("hours", "minutes"), + }; + if hours > 0 { + format!("{}{} {}{}", hours, h, minutes, m) + } else { + format!("{}{}", minutes, m) + } + } + + pub fn parse(s: &str) -> Result { + let digits = take_digits(s.to_owned()); + let value = digits.parse::().map_err(|_| ParseError)?; + Ok(Duration { + value: value * 60. * si::S, + }) + } + + fn hours_and_minutes(&self) -> (i64, i64) { + let minutes: i64 = (self.value.value_unsafe / 60.).round() as i64; + let hours: i64 = minutes / 60; + let minutes = minutes - (hours * 60); + (hours, minutes) + } +} + +impl std::ops::Add for Duration { + type Output = Duration; + fn add(self, rside: Self) -> Self::Output { + Self::Output::from(self.value + rside.value) + } +} + +impl std::ops::Sub for Duration { + type Output = Duration; + fn sub(self, rside: Self) -> Self::Output { + Self::Output::from(self.value - rside.value) + } +} + +impl std::ops::Deref for Duration { + type Target = si::Second; + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl From> for Duration { + fn from(value: si::Second) -> Self { + Self { value } + } +} + +fn take_digits(s: String) -> String { + s.chars() + .take_while(|t| t.is_ascii_digit()) + .collect::() +}