Add support for degrees Fahrenheit
This commit is contained in:
parent
b3fa9a01f1
commit
81082fd11d
|
@ -2,12 +2,13 @@ use crate::units::{Metric, MetricQuantity, NonMetric, NonMetricQuantity};
|
||||||
|
|
||||||
pub fn convert(from: NonMetricQuantity) -> MetricQuantity {
|
pub fn convert(from: NonMetricQuantity) -> MetricQuantity {
|
||||||
let conversion = get_conversion(from.unit);
|
let conversion = get_conversion(from.unit);
|
||||||
let amount = from.amount * conversion.to.amount / conversion.from;
|
let amount = (from.amount - conversion.offset) * conversion.to.amount / conversion.from;
|
||||||
let unit = conversion.to.unit;
|
let unit = conversion.to.unit;
|
||||||
MetricQuantity { amount, unit }
|
MetricQuantity { amount, unit }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Conversion {
|
struct Conversion {
|
||||||
|
offset: f64,
|
||||||
from: f64,
|
from: f64,
|
||||||
to: MetricQuantity,
|
to: MetricQuantity,
|
||||||
}
|
}
|
||||||
|
@ -22,34 +23,47 @@ fn get_conversion(unit: NonMetric) -> Conversion {
|
||||||
match unit {
|
match unit {
|
||||||
// Length
|
// Length
|
||||||
NonMetric::Inch => Conversion {
|
NonMetric::Inch => Conversion {
|
||||||
|
offset: 0.0,
|
||||||
from: inch_from,
|
from: inch_from,
|
||||||
to: MetricQuantity { amount: inch_to, unit: Metric::Metre },
|
to: MetricQuantity { amount: inch_to, unit: Metric::Metre },
|
||||||
},
|
},
|
||||||
NonMetric::Foot => Conversion {
|
NonMetric::Foot => Conversion {
|
||||||
|
offset: 0.0,
|
||||||
from: inch_from,
|
from: inch_from,
|
||||||
to: MetricQuantity { amount: 12.0 * inch_to, unit: Metric::Metre },
|
to: MetricQuantity { amount: 12.0 * inch_to, unit: Metric::Metre },
|
||||||
},
|
},
|
||||||
NonMetric::Yard => Conversion {
|
NonMetric::Yard => Conversion {
|
||||||
|
offset: 0.0,
|
||||||
from: inch_from,
|
from: inch_from,
|
||||||
to: MetricQuantity { amount: 3.0 * 12.0 * inch_to, unit: Metric::Metre },
|
to: MetricQuantity { amount: 3.0 * 12.0 * inch_to, unit: Metric::Metre },
|
||||||
},
|
},
|
||||||
NonMetric::Mile => Conversion {
|
NonMetric::Mile => Conversion {
|
||||||
|
offset: 0.0,
|
||||||
from: inch_from,
|
from: inch_from,
|
||||||
to: MetricQuantity { amount: 1760.0 * 3.0 * 12.0 * inch_to, unit: Metric::Metre },
|
to: MetricQuantity { amount: 1760.0 * 3.0 * 12.0 * inch_to, unit: Metric::Metre },
|
||||||
},
|
},
|
||||||
// Weight
|
// Weight
|
||||||
NonMetric::Ounce => Conversion {
|
NonMetric::Ounce => Conversion {
|
||||||
|
offset: 0.0,
|
||||||
from: 16.0 * pound_from,
|
from: 16.0 * pound_from,
|
||||||
to: MetricQuantity { amount: pound_to, unit: Metric::Gram },
|
to: MetricQuantity { amount: pound_to, unit: Metric::Gram },
|
||||||
},
|
},
|
||||||
NonMetric::Pound => Conversion {
|
NonMetric::Pound => Conversion {
|
||||||
|
offset: 0.0,
|
||||||
from: pound_from,
|
from: pound_from,
|
||||||
to: MetricQuantity { amount: pound_to, unit: Metric::Gram },
|
to: MetricQuantity { amount: pound_to, unit: Metric::Gram },
|
||||||
},
|
},
|
||||||
NonMetric::Stone => Conversion {
|
NonMetric::Stone => Conversion {
|
||||||
|
offset: 0.0,
|
||||||
from: pound_from,
|
from: pound_from,
|
||||||
to: MetricQuantity { amount: 14.0 * pound_to, unit: Metric::Gram },
|
to: MetricQuantity { amount: 14.0 * pound_to, unit: Metric::Gram },
|
||||||
},
|
},
|
||||||
|
// Temperature
|
||||||
|
NonMetric::Fahrenheit => Conversion {
|
||||||
|
offset: 32.0,
|
||||||
|
from: 9.0,
|
||||||
|
to: MetricQuantity { amount: 5.0, unit: Metric::Celcius },
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +94,24 @@ mod test {
|
||||||
run_tests(&tests, Metric::Gram);
|
run_tests(&tests, Metric::Gram);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn temperature() {
|
||||||
|
assert_eq!(convert(NonMetricQuantity {
|
||||||
|
amount: -40.0,
|
||||||
|
unit: NonMetric::Fahrenheit,
|
||||||
|
}), MetricQuantity {
|
||||||
|
amount: -40.0,
|
||||||
|
unit: Metric::Celcius,
|
||||||
|
});
|
||||||
|
assert_eq!(convert(NonMetricQuantity {
|
||||||
|
amount: 32.0,
|
||||||
|
unit: NonMetric::Fahrenheit,
|
||||||
|
}), MetricQuantity {
|
||||||
|
amount: 0.0,
|
||||||
|
unit: Metric::Celcius,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn run_tests(tests: &[Test], unit: Metric) {
|
fn run_tests(tests: &[Test], unit: Metric) {
|
||||||
for test in tests {
|
for test in tests {
|
||||||
let from = NonMetricQuantity {
|
let from = NonMetricQuantity {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::units::{Metric, MetricQuantity};
|
use crate::units::{Metric, MetricQuantity};
|
||||||
|
|
||||||
pub fn format(quantity: MetricQuantity) -> String {
|
pub fn format(quantity: MetricQuantity) -> String {
|
||||||
let SiUnit(multiplier, unit) = si_unit(quantity);
|
let PrefixedUnit(multiplier, unit) = prefixed_unit(quantity);
|
||||||
let amount = quantity.amount / multiplier;
|
let amount = quantity.amount / multiplier;
|
||||||
let amount = format_number(amount);
|
let amount = format_number(amount);
|
||||||
format!("{amount} {unit}")
|
format!("{amount} {unit}")
|
||||||
|
@ -59,30 +59,31 @@ fn format_number(number: f64) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct SiUnit(f64, &'static str);
|
struct PrefixedUnit(f64, &'static str);
|
||||||
|
|
||||||
fn si_unit(quantity: MetricQuantity) -> SiUnit {
|
fn prefixed_unit(quantity: MetricQuantity) -> PrefixedUnit {
|
||||||
let absolute = quantity.amount.abs();
|
let absolute = quantity.amount.abs();
|
||||||
|
|
||||||
match quantity.unit {
|
match quantity.unit {
|
||||||
Metric::Metre => {
|
Metric::Metre => {
|
||||||
if absolute >= 1000.0 {
|
if absolute >= 1000.0 {
|
||||||
return SiUnit(1000.0, "km");
|
return PrefixedUnit(1000.0, "km");
|
||||||
} else if absolute >= 1.0 {
|
} else if absolute >= 1.0 {
|
||||||
return SiUnit(1.0, "m");
|
return PrefixedUnit(1.0, "m");
|
||||||
} else if absolute >= 0.01 {
|
} else if absolute >= 0.01 {
|
||||||
return SiUnit(0.01, "cm");
|
return PrefixedUnit(0.01, "cm");
|
||||||
} else {
|
} else {
|
||||||
return SiUnit(0.001, "mm");
|
return PrefixedUnit(0.001, "mm");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Metric::Gram => {
|
Metric::Gram => {
|
||||||
if absolute >= 1000.0 {
|
if absolute >= 1000.0 {
|
||||||
return SiUnit(1000.0, "kg");
|
return PrefixedUnit(1000.0, "kg");
|
||||||
} else {
|
} else {
|
||||||
return SiUnit(1.0, "g");
|
return PrefixedUnit(1.0, "g");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Metric::Celcius => PrefixedUnit(1.0, "°C"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +118,11 @@ mod test {
|
||||||
amount: 3_200.0,
|
amount: 3_200.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
assert_eq!("1 000 °C", &format(MetricQuantity {
|
||||||
|
amount: 1_000.0,
|
||||||
|
unit: Metric::Celcius,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -149,92 +155,105 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn units() {
|
fn units() {
|
||||||
assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(0.001, "mm"), prefixed_unit(MetricQuantity {
|
||||||
amount: 0.0001,
|
amount: 0.0001,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(0.001, "mm"), prefixed_unit(MetricQuantity {
|
||||||
amount: 0.001,
|
amount: 0.001,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(0.01, "cm"), prefixed_unit(MetricQuantity {
|
||||||
amount: 0.01,
|
amount: 0.01,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(0.01, "cm"), prefixed_unit(MetricQuantity {
|
||||||
amount: 0.1,
|
amount: 0.1,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity {
|
||||||
amount: 1.0,
|
amount: 1.0,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity {
|
||||||
amount: 10.0,
|
amount: 10.0,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity {
|
||||||
amount: 100.0,
|
amount: 100.0,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1000.0, "km"), prefixed_unit(MetricQuantity {
|
||||||
amount: 1000.0,
|
amount: 1000.0,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1000.0, "km"), prefixed_unit(MetricQuantity {
|
||||||
amount: 10_000.0,
|
amount: 10_000.0,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(0.001, "mm"), prefixed_unit(MetricQuantity {
|
||||||
amount: -0.001,
|
amount: -0.001,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(0.01, "cm"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(0.01, "cm"), prefixed_unit(MetricQuantity {
|
||||||
amount: -0.01,
|
amount: -0.01,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1.0, "m"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "m"), prefixed_unit(MetricQuantity {
|
||||||
amount: -1.0,
|
amount: -1.0,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1000.0, "km"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1000.0, "km"), prefixed_unit(MetricQuantity {
|
||||||
amount: -1000.0,
|
amount: -1000.0,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity {
|
||||||
amount: 0.1,
|
amount: 0.1,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity {
|
||||||
amount: 1.0,
|
amount: 1.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity {
|
||||||
amount: 10.0,
|
amount: 10.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity {
|
||||||
amount: 100.0,
|
amount: 100.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1000.0, "kg"), prefixed_unit(MetricQuantity {
|
||||||
amount: 1000.0,
|
amount: 1000.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1000.0, "kg"), prefixed_unit(MetricQuantity {
|
||||||
amount: 10_1000.0,
|
amount: 10_1000.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert_eq!(SiUnit(1.0, "g"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1.0, "g"), prefixed_unit(MetricQuantity {
|
||||||
amount: -1.0,
|
amount: -1.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
assert_eq!(SiUnit(1000.0, "kg"), si_unit(MetricQuantity {
|
assert_eq!(PrefixedUnit(1000.0, "kg"), prefixed_unit(MetricQuantity {
|
||||||
amount: -1000.0,
|
amount: -1000.0,
|
||||||
unit: Metric::Gram,
|
unit: Metric::Gram,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
assert_eq!(PrefixedUnit(1.0, "°C"), prefixed_unit(MetricQuantity {
|
||||||
|
amount: 0.0001,
|
||||||
|
unit: Metric::Celcius,
|
||||||
|
}));
|
||||||
|
assert_eq!(PrefixedUnit(1.0, "°C"), prefixed_unit(MetricQuantity {
|
||||||
|
amount: -1.0,
|
||||||
|
unit: Metric::Celcius,
|
||||||
|
}));
|
||||||
|
assert_eq!(PrefixedUnit(1.0, "°C"), prefixed_unit(MetricQuantity {
|
||||||
|
amount: 1_000.0,
|
||||||
|
unit: Metric::Celcius,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,8 @@ fn unit_to_name(unit: NonMetric) -> &'static str {
|
||||||
NonMetric::Ounce => "ounces",
|
NonMetric::Ounce => "ounces",
|
||||||
NonMetric::Pound => "pounds",
|
NonMetric::Pound => "pounds",
|
||||||
NonMetric::Stone => "stones",
|
NonMetric::Stone => "stones",
|
||||||
|
// Temperature
|
||||||
|
NonMetric::Fahrenheit => "degrees Fahrenheit",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,5 +100,9 @@ mod test {
|
||||||
assert_eq!(run("1 oz"), Ok("28.35 g".to_string()));
|
assert_eq!(run("1 oz"), Ok("28.35 g".to_string()));
|
||||||
assert_eq!(run("1 lb"), Ok("453.6 g".to_string()));
|
assert_eq!(run("1 lb"), Ok("453.6 g".to_string()));
|
||||||
assert_eq!(run("1 st"), Ok("6.35 kg".to_string()));
|
assert_eq!(run("1 st"), Ok("6.35 kg".to_string()));
|
||||||
|
// Temperature
|
||||||
|
assert_eq!(run("-40 °F"), Ok("-40 °C".to_string()));
|
||||||
|
assert_eq!(run("0 °F"), Ok("-17.78 °C".to_string()));
|
||||||
|
assert_eq!(run("32 °F"), Ok("0 °C".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,10 @@ fn parse_unit(input: String) -> Result<NonMetric, ParseError> {
|
||||||
"stones" => Ok(NonMetric::Stone),
|
"stones" => Ok(NonMetric::Stone),
|
||||||
"st" => Ok(NonMetric::Stone),
|
"st" => Ok(NonMetric::Stone),
|
||||||
|
|
||||||
|
// Temperature
|
||||||
|
"°F" => Ok(NonMetric::Fahrenheit),
|
||||||
|
"F" => Ok(NonMetric::Fahrenheit),
|
||||||
|
|
||||||
_ => Err(ParseError::UnknownUnit(input)),
|
_ => Err(ParseError::UnknownUnit(input)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,6 +246,10 @@ mod test {
|
||||||
assert_eq!(parse_unit("stones".to_string()), Ok(NonMetric::Stone));
|
assert_eq!(parse_unit("stones".to_string()), Ok(NonMetric::Stone));
|
||||||
assert_eq!(parse_unit("st".to_string()), Ok(NonMetric::Stone));
|
assert_eq!(parse_unit("st".to_string()), Ok(NonMetric::Stone));
|
||||||
|
|
||||||
|
// Temperature
|
||||||
|
assert_eq!(parse_unit("°F".to_string()), Ok(NonMetric::Fahrenheit));
|
||||||
|
assert_eq!(parse_unit("F".to_string()), Ok(NonMetric::Fahrenheit));
|
||||||
|
|
||||||
// Unknown unit
|
// Unknown unit
|
||||||
assert_eq!(parse_unit("hutenosa".to_string()), Err(ParseError::UnknownUnit("hutenosa".to_string())));
|
assert_eq!(parse_unit("hutenosa".to_string()), Err(ParseError::UnknownUnit("hutenosa".to_string())));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
pub enum Metric {
|
pub enum Metric {
|
||||||
Metre,
|
Metre,
|
||||||
Gram,
|
Gram,
|
||||||
|
Celcius,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
@ -15,6 +16,8 @@ pub enum NonMetric {
|
||||||
Ounce,
|
Ounce,
|
||||||
Pound,
|
Pound,
|
||||||
Stone,
|
Stone,
|
||||||
|
// Temperature
|
||||||
|
Fahrenheit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
Loading…
Reference in New Issue