use std::ops::{Div, Mul};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::{constants, SpeedUnit};
use super::{Duration, DurationUnit, Measurement, PhysicalQuantity, Speed, UnitOfMeasure};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub enum LengthUnit {
Centimeters,
Meters,
Kilometers,
Inches,
Feet,
NauticalMiles,
StatuteMiles,
}
impl UnitOfMeasure<f32> for LengthUnit {
fn quantity() -> PhysicalQuantity {
PhysicalQuantity::Length
}
fn si() -> Self {
Self::Meters
}
fn symbol(&self) -> &'static str {
match self {
Self::Centimeters => "cm",
Self::Meters => "m",
Self::Kilometers => "km",
Self::Inches => "in",
Self::Feet => "ft",
Self::NauticalMiles => "NM",
Self::StatuteMiles => "SM",
}
}
fn from_si(value: f32, to: &Self) -> f32 {
match to {
Self::Centimeters => value * 100.0,
Self::Meters => value,
Self::Kilometers => value / 1000.0,
Self::Inches => value / constants::INCH_IN_METER,
Self::Feet => value / constants::FEET_IN_METER,
Self::NauticalMiles => value / constants::NAUTICAL_MILE_IN_METER,
Self::StatuteMiles => value / constants::STATUTE_MILE_IN_METER,
}
}
fn to_si(&self, value: &f32) -> f32 {
match self {
Self::Centimeters => value / 100.0,
Self::Meters => *value,
Self::Kilometers => value * 1000.0,
Self::Inches => value * constants::INCH_IN_METER,
Self::Feet => value * constants::FEET_IN_METER,
Self::NauticalMiles => value * constants::NAUTICAL_MILE_IN_METER,
Self::StatuteMiles => value * constants::STATUTE_MILE_IN_METER,
}
}
}
pub type Length = Measurement<f32, LengthUnit>;
impl Length {
pub fn cm(value: f32) -> Self {
Self {
value,
unit: LengthUnit::Centimeters,
}
}
pub fn m(value: f32) -> Self {
Self {
value,
unit: LengthUnit::Meters,
}
}
pub fn km(value: f32) -> Self {
Self {
value,
unit: LengthUnit::Kilometers,
}
}
pub fn inch(value: f32) -> Self {
Self {
value,
unit: LengthUnit::Inches,
}
}
pub fn ft(value: f32) -> Self {
Self {
value,
unit: LengthUnit::Feet,
}
}
pub fn nm(value: f32) -> Self {
Self {
value,
unit: LengthUnit::NauticalMiles,
}
}
pub fn sm(value: f32) -> Self {
Self {
value,
unit: LengthUnit::StatuteMiles,
}
}
}
impl Div<Speed> for Length {
type Output = Duration;
fn div(self, rhs: Speed) -> Self::Output {
let s = self.to_si() / rhs.to_si();
Duration::from_si(s.round() as u32, DurationUnit::Seconds)
}
}
impl Div<Duration> for Length {
type Output = Speed;
fn div(self, rhs: Duration) -> Self::Output {
let unit = match self.unit {
LengthUnit::NauticalMiles => SpeedUnit::Knots,
_ => SpeedUnit::MetersPerSecond,
};
let value = self.to_si() / rhs.to_si() as f32;
Speed::from_si(value, unit)
}
}
impl Mul<Duration> for Speed {
type Output = Length;
fn mul(self, rhs: Duration) -> Self::Output {
let m = self.to_si() * rhs.to_si() as f32;
let unit = match self.unit {
SpeedUnit::Knots => LengthUnit::NauticalMiles,
_ => LengthUnit::Meters,
};
Length::from_si(m, unit)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn length_shows_symbol() {
let m = Length::m(0.0);
let nm = Length::nm(0.0);
assert_eq!(m.symbol(), "m");
assert_eq!(nm.symbol(), "NM");
}
#[test]
fn length_to_si() {
let nm = Length::nm(1.0);
let inch = Length::inch(1.0);
assert_eq!(nm.to_si(), constants::NAUTICAL_MILE_IN_METER);
assert_eq!(inch.to_si(), constants::INCH_IN_METER);
}
#[test]
fn convert_length() {
assert_eq!(Length::m(0.0254), Length::inch(1.0));
}
#[test]
fn convert_nm_to_m() {
assert_eq!(Length::nm(1.0), Length::m(1852.0));
}
#[test]
fn div_length_by_speed() {
let time = Length::nm(1.0) / Speed::kt(1.0);
assert_eq!(time, Duration::s(3600));
}
}