280 lines
8.0 KiB
Rust
280 lines
8.0 KiB
Rust
use crate::units::{Metric, MetricQuantity, NonMetric, NonMetricQuantity};
|
|
|
|
pub fn convert(from: NonMetricQuantity) -> MetricQuantity {
|
|
let conversion = get_conversion(from.unit);
|
|
let amount = (from.amount - conversion.offset) * conversion.to.amount / conversion.from;
|
|
let unit = conversion.to.unit;
|
|
MetricQuantity { amount, unit }
|
|
}
|
|
|
|
struct Conversion {
|
|
offset: f64,
|
|
from: f64,
|
|
to: MetricQuantity,
|
|
}
|
|
|
|
fn get_conversion(unit: NonMetric) -> Conversion {
|
|
const INCH_FROM: f64 = 10_000.0;
|
|
const INCH_TO: f64 = 254.0;
|
|
|
|
const POUND_FROM: f64 = 100_000.0;
|
|
const POUND_TO: f64 = 45359237.0;
|
|
|
|
const IMPERIAL_GALLON_FROM: f64 = 100_000.0;
|
|
const IMPERIAL_GALLON_TO: f64 = 454609.0;
|
|
|
|
const US_GALLON_FROM: f64 = INCH_FROM * INCH_FROM * INCH_FROM;
|
|
const US_GALLON_TO: f64 = 231.0 * INCH_TO * INCH_TO * INCH_TO * 1000.0;
|
|
|
|
match unit {
|
|
// Length
|
|
NonMetric::Inch => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM,
|
|
to: MetricQuantity { amount: INCH_TO, unit: Metric::Metre },
|
|
},
|
|
NonMetric::Foot => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM,
|
|
to: MetricQuantity { amount: 12.0 * INCH_TO, unit: Metric::Metre },
|
|
},
|
|
NonMetric::Yard => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM,
|
|
to: MetricQuantity { amount: 3.0 * 12.0 * INCH_TO, unit: Metric::Metre },
|
|
},
|
|
NonMetric::Mile => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM,
|
|
to: MetricQuantity { amount: 1760.0 * 3.0 * 12.0 * INCH_TO, unit: Metric::Metre },
|
|
},
|
|
// Mass
|
|
NonMetric::Ounce => Conversion {
|
|
offset: 0.0,
|
|
from: 16.0 * POUND_FROM,
|
|
to: MetricQuantity { amount: POUND_TO, unit: Metric::Gram },
|
|
},
|
|
NonMetric::Pound => Conversion {
|
|
offset: 0.0,
|
|
from: POUND_FROM,
|
|
to: MetricQuantity { amount: POUND_TO, unit: Metric::Gram },
|
|
},
|
|
NonMetric::Stone => Conversion {
|
|
offset: 0.0,
|
|
from: POUND_FROM,
|
|
to: MetricQuantity { amount: 14.0 * POUND_TO, unit: Metric::Gram },
|
|
},
|
|
NonMetric::ShortTon => Conversion {
|
|
offset: 0.0,
|
|
from: POUND_FROM,
|
|
to: MetricQuantity { amount: 2000.0 * POUND_TO, unit: Metric::Gram },
|
|
},
|
|
NonMetric::LongTon => Conversion {
|
|
offset: 0.0,
|
|
from: POUND_FROM,
|
|
to: MetricQuantity { amount: 2240.0 * POUND_TO, unit: Metric::Gram },
|
|
},
|
|
// Temperature
|
|
NonMetric::Fahrenheit => Conversion {
|
|
offset: 32.0,
|
|
from: 9.0,
|
|
to: MetricQuantity { amount: 5.0, unit: Metric::Celsius },
|
|
},
|
|
// Area
|
|
NonMetric::SquareInch => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: INCH_TO * INCH_TO, unit: Metric::SquareMetre },
|
|
},
|
|
NonMetric::SquareFoot => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: 12.0 * INCH_TO * 12.0 * INCH_TO, unit: Metric::SquareMetre },
|
|
},
|
|
NonMetric::SquareYard => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: 3.0 * 12.0 * INCH_TO * 3.0 * 12.0 * INCH_TO, unit: Metric::SquareMetre },
|
|
},
|
|
NonMetric::Acre => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: 43_560.0 * 12.0 * INCH_TO * 12.0 * INCH_TO, unit: Metric::SquareMetre },
|
|
},
|
|
NonMetric::SquareMile => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: 1760.0 * 3.0 * 12.0 * INCH_TO * 1760.0 * 3.0 * 12.0 * INCH_TO, unit: Metric::SquareMetre },
|
|
},
|
|
// Volume
|
|
NonMetric::CubicInch => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: INCH_TO * INCH_TO * INCH_TO, unit: Metric::CubicMetre },
|
|
},
|
|
NonMetric::CubicFoot => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: 12.0 * INCH_TO * 12.0 * INCH_TO * 12.0 * INCH_TO, unit: Metric::CubicMetre },
|
|
},
|
|
NonMetric::CubicYard => Conversion {
|
|
offset: 0.0,
|
|
from: INCH_FROM * INCH_FROM * INCH_FROM,
|
|
to: MetricQuantity { amount: 3.0 * 12.0 * INCH_TO * 3.0 * 12.0 * INCH_TO * 3.0 * 12.0 * INCH_TO, unit: Metric::CubicMetre },
|
|
},
|
|
// Fluid volume
|
|
NonMetric::ImperialFluidOunce => Conversion {
|
|
offset: 0.0,
|
|
from: 20.0 * 2.0 * 4.0 * IMPERIAL_GALLON_FROM,
|
|
to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::ImperialPint => Conversion {
|
|
offset: 0.0,
|
|
from: 2.0 * 4.0 * IMPERIAL_GALLON_FROM,
|
|
to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::ImperialQuart => Conversion {
|
|
offset: 0.0,
|
|
from: 4.0 * IMPERIAL_GALLON_FROM,
|
|
to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::ImperialGallon => Conversion {
|
|
offset: 0.0,
|
|
from: IMPERIAL_GALLON_FROM,
|
|
to: MetricQuantity { amount: IMPERIAL_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::USTeaspoon => Conversion {
|
|
offset: 0.0,
|
|
from: 6.0 * 16.0 * 2.0 * 4.0 * US_GALLON_FROM,
|
|
to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::USTablespoon => Conversion {
|
|
offset: 0.0,
|
|
from: 2.0 * 16.0 * 2.0 * 4.0 * US_GALLON_FROM,
|
|
to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::USFluidOunce => Conversion {
|
|
offset: 0.0,
|
|
from: 16.0 * 2.0 * 4.0 * US_GALLON_FROM,
|
|
to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::USCup => Conversion {
|
|
offset: 0.0,
|
|
from: 2.0 * 2.0 * 4.0 * US_GALLON_FROM,
|
|
to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::USLiquidPint => Conversion {
|
|
offset: 0.0,
|
|
from: 2.0 * 4.0 * US_GALLON_FROM,
|
|
to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::USLiquidQuart => Conversion {
|
|
offset: 0.0,
|
|
from: 4.0 * US_GALLON_FROM,
|
|
to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
NonMetric::USGallon => Conversion {
|
|
offset: 0.0,
|
|
from: US_GALLON_FROM,
|
|
to: MetricQuantity { amount: US_GALLON_TO, unit: Metric::Litre },
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
struct Test(NonMetric, f64);
|
|
|
|
#[test]
|
|
fn length() {
|
|
run_tests(Metric::Metre, &[
|
|
Test(NonMetric::Inch, 0.0254),
|
|
Test(NonMetric::Foot, 0.3048),
|
|
Test(NonMetric::Yard, 0.9144),
|
|
Test(NonMetric::Mile, 1609.344),
|
|
]);
|
|
}
|
|
|
|
#[test]
|
|
fn mass() {
|
|
run_tests(Metric::Gram, &[
|
|
Test(NonMetric::Ounce, 28.349523125),
|
|
Test(NonMetric::Pound, 453.59237),
|
|
Test(NonMetric::Stone, 6350.29318),
|
|
Test(NonMetric::ShortTon, 907184.74),
|
|
Test(NonMetric::LongTon, 1016046.9088),
|
|
]);
|
|
}
|
|
|
|
#[test]
|
|
fn temperature() {
|
|
assert_eq!(convert(NonMetricQuantity {
|
|
amount: -40.0,
|
|
unit: NonMetric::Fahrenheit,
|
|
}), MetricQuantity {
|
|
amount: -40.0,
|
|
unit: Metric::Celsius,
|
|
});
|
|
assert_eq!(convert(NonMetricQuantity {
|
|
amount: 32.0,
|
|
unit: NonMetric::Fahrenheit,
|
|
}), MetricQuantity {
|
|
amount: 0.0,
|
|
unit: Metric::Celsius,
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn area() {
|
|
run_tests(Metric::SquareMetre, &[
|
|
Test(NonMetric::SquareInch, 0.00064516),
|
|
Test(NonMetric::SquareFoot, 0.09290304),
|
|
Test(NonMetric::SquareYard, 0.83612736),
|
|
Test(NonMetric::Acre, 4046.8564224),
|
|
Test(NonMetric::SquareMile, 2589988.110336),
|
|
]);
|
|
}
|
|
|
|
#[test]
|
|
fn volume() {
|
|
run_tests(Metric::CubicMetre, &[
|
|
Test(NonMetric::CubicInch, 1.6387064e-5),
|
|
Test(NonMetric::CubicFoot, 0.028316846592),
|
|
Test(NonMetric::CubicYard, 0.764554857984),
|
|
]);
|
|
}
|
|
|
|
#[test]
|
|
fn fluid_volume() {
|
|
run_tests(Metric::Litre, &[
|
|
Test(NonMetric::ImperialFluidOunce, 0.0284130625),
|
|
Test(NonMetric::ImperialPint, 0.56826125),
|
|
Test(NonMetric::ImperialQuart, 1.1365225),
|
|
Test(NonMetric::ImperialGallon, 4.54609),
|
|
Test(NonMetric::USTeaspoon, 0.00492892159375),
|
|
Test(NonMetric::USTablespoon, 0.01478676478125),
|
|
Test(NonMetric::USFluidOunce, 0.0295735295625),
|
|
Test(NonMetric::USCup, 0.2365882365),
|
|
Test(NonMetric::USLiquidPint, 0.473176473),
|
|
Test(NonMetric::USLiquidQuart, 0.946352946),
|
|
Test(NonMetric::USGallon, 3.785411784),
|
|
]);
|
|
}
|
|
|
|
fn run_tests(unit: Metric, tests: &[Test]) {
|
|
for test in tests {
|
|
let from = NonMetricQuantity {
|
|
amount: 1.0,
|
|
unit: test.0,
|
|
};
|
|
let to = MetricQuantity {
|
|
amount: test.1,
|
|
unit: unit,
|
|
};
|
|
assert_eq!(convert(from), to);
|
|
}
|
|
}
|
|
}
|