mirror of https://github.com/rm-dr/daisy
Added prefix-generating macros
parent
78b0262f61
commit
3948f10bd7
|
@ -5,6 +5,7 @@ use crate::quantity::Quantity;
|
|||
use super::UnitBase;
|
||||
use super::Prefix;
|
||||
use super::Unit;
|
||||
use super::unit_db;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -28,27 +29,45 @@ impl PartialEq for FreeUnit {
|
|||
}
|
||||
|
||||
|
||||
macro_rules! quick_base_factor {
|
||||
(float, $u:expr, $s:expr, $( ($x:expr, $p:expr) ),* ) => {
|
||||
macro_rules! unpack_base_factor {
|
||||
(
|
||||
$unit:expr,
|
||||
$display_string:expr,
|
||||
base
|
||||
) => { None };
|
||||
|
||||
(
|
||||
$unit:expr,
|
||||
$display_string:expr,
|
||||
float,
|
||||
$value:expr,
|
||||
$( ($u:expr, $p:expr) ),*
|
||||
) => {
|
||||
Some(Quantity {
|
||||
scalar: Scalar::new_float_from_string($s).unwrap(),
|
||||
scalar: Scalar::new_float_from_string($value).unwrap(),
|
||||
unit: Unit::from_array(&[
|
||||
$(
|
||||
(FreeUnit::from_base($x), Scalar::new_rational($p).unwrap()),
|
||||
(FreeUnit::from_base($u), Scalar::new_rational($p).unwrap()),
|
||||
)*
|
||||
(FreeUnit::from_base($u), Scalar::new_rational(-1f64).unwrap())
|
||||
(FreeUnit::from_base($unit), Scalar::new_rational(-1f64).unwrap())
|
||||
])
|
||||
})
|
||||
};
|
||||
|
||||
(rational, $u:expr, $s:expr, $( ($x:expr, $p:expr) ),* ) => {
|
||||
(
|
||||
$unit:expr,
|
||||
$display_string:expr,
|
||||
rational,
|
||||
$value:expr,
|
||||
$( ($u:expr, $p:expr) ),*
|
||||
) => {
|
||||
Some(Quantity {
|
||||
scalar: Scalar::new_float_from_string($s).unwrap(),
|
||||
scalar: Scalar::new_rational_from_string($value).unwrap(),
|
||||
unit: Unit::from_array(&[
|
||||
$(
|
||||
(FreeUnit::from_base($x), Scalar::new_rational($p).unwrap()),
|
||||
(FreeUnit::from_base($u), Scalar::new_rational($p).unwrap()),
|
||||
)*
|
||||
(FreeUnit::from_base($u), Scalar::new_rational(-1f64).unwrap())
|
||||
(FreeUnit::from_base($unit), Scalar::new_rational(-1f64).unwrap())
|
||||
])
|
||||
})
|
||||
};
|
||||
|
@ -70,57 +89,8 @@ impl FreeUnit {
|
|||
|
||||
|
||||
pub fn to_base_factor(&self) -> Quantity {
|
||||
let q = match self.base {
|
||||
// Returns the unit we need to multiply by to get a base
|
||||
// unit, or `None` if this is already a base unit.
|
||||
//
|
||||
// Example:
|
||||
// 1 foot = 0.3048 m,
|
||||
// so 1 ft * (0.3084 m / ft) will give meters.
|
||||
//
|
||||
// The units here MUST be in terms of base units.
|
||||
// If they aren't, things will break.
|
||||
|
||||
UnitBase::Foot => quick_base_factor!(float,
|
||||
UnitBase::Foot,
|
||||
"0.3048",
|
||||
(UnitBase::Meter, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Inch => quick_base_factor!(float,
|
||||
UnitBase::Inch,
|
||||
"0.0254",
|
||||
(UnitBase::Meter, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Mile => quick_base_factor!(rational,
|
||||
UnitBase::Mile,
|
||||
"1609",
|
||||
(UnitBase::Meter, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Minute => quick_base_factor!(rational,
|
||||
UnitBase::Minute,
|
||||
"60",
|
||||
(UnitBase::Second, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Hour => quick_base_factor!(rational,
|
||||
UnitBase::Hour,
|
||||
"3600",
|
||||
(UnitBase::Second, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Day => quick_base_factor!(rational,
|
||||
UnitBase::Day,
|
||||
"86400",
|
||||
(UnitBase::Second, 1f64)
|
||||
),
|
||||
|
||||
// Only base units should be missing a conversion factor.
|
||||
_ => None
|
||||
};
|
||||
|
||||
let q = unit_db!(self.base, unpack_base_factor);
|
||||
let mut q = q.unwrap_or(Quantity::new_rational_from_string("1").unwrap());
|
||||
|
||||
let mut p = self.prefix.to_ratio();
|
||||
|
@ -133,26 +103,18 @@ impl FreeUnit {
|
|||
}
|
||||
|
||||
|
||||
|
||||
macro_rules! unpack_string {
|
||||
(
|
||||
$u:expr, $s:expr,
|
||||
$( $_:expr ),*
|
||||
) => { $s };
|
||||
}
|
||||
|
||||
impl ToString for FreeUnit {
|
||||
fn to_string(&self) -> String {
|
||||
let s = match self.base {
|
||||
UnitBase::Second => "s",
|
||||
UnitBase::Meter => "m",
|
||||
UnitBase::Gram => "g",
|
||||
UnitBase::Ampere => "a",
|
||||
UnitBase::Kelvin => "k",
|
||||
UnitBase::Mole => "mol",
|
||||
UnitBase::Candela => "c",
|
||||
|
||||
UnitBase::Foot => "ft",
|
||||
UnitBase::Inch => "in",
|
||||
UnitBase::Mile => "mile",
|
||||
|
||||
UnitBase::Hour => "hour",
|
||||
UnitBase::Minute => "min",
|
||||
UnitBase::Day => "day",
|
||||
};
|
||||
|
||||
let s = unit_db!(self.base, unpack_string);
|
||||
let p = self.prefix.to_string();
|
||||
|
||||
format!("{p}{s}")
|
||||
|
|
|
@ -9,7 +9,6 @@ pub use unit::Unit;
|
|||
pub use freeunit::FreeUnit;
|
||||
|
||||
|
||||
|
||||
#[derive(Hash)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -38,3 +37,159 @@ pub enum UnitBase {
|
|||
}
|
||||
|
||||
|
||||
// SI prefix list:
|
||||
// ("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
|
||||
// X macro, used in Unit.from_string()
|
||||
//
|
||||
// Format is as follows:
|
||||
// (Unit, string from, (prefixes_to_generate))
|
||||
// Prefixes must be valid prefixes as defined in
|
||||
// Prefix::str_to_prefix.
|
||||
pub (self) use prefix::str_to_prefix;
|
||||
macro_rules! fromstring_db {
|
||||
($X:ident) => {
|
||||
$X!(
|
||||
// No prefix
|
||||
(UnitBase::Meter, "meter"),
|
||||
(UnitBase::Foot, "ft"),
|
||||
(UnitBase::Mile, "mile"),
|
||||
(UnitBase::Hour, "hour"),
|
||||
(UnitBase::Minute, "min"),
|
||||
(UnitBase::Day, "day"),
|
||||
(UnitBase::Second, "sec"),
|
||||
|
||||
|
||||
(UnitBase::Meter, "m",
|
||||
("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
),
|
||||
|
||||
(UnitBase::Second, "s",
|
||||
("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
),
|
||||
|
||||
(UnitBase::Gram, "g",
|
||||
("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
),
|
||||
|
||||
(UnitBase::Ampere, "a",
|
||||
("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
),
|
||||
|
||||
(UnitBase::Kelvin, "k",
|
||||
("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
),
|
||||
|
||||
(UnitBase::Mole, "mol",
|
||||
("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
),
|
||||
|
||||
(UnitBase::Candela, "c",
|
||||
("Q","R","Y","Z","E","P","T","G","M","k","h","da","d","c","m","u","n","p","f","a","z","y","r","q")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
pub (self) use fromstring_db;
|
||||
|
||||
|
||||
// X macro, used in the following functions:
|
||||
// - FreeUnit.to_base_factor()
|
||||
// - FreeUnit.to_string()
|
||||
//
|
||||
// Read below comments for explanation.
|
||||
macro_rules! unit_db {
|
||||
($a:expr, $X:ident) => {
|
||||
match $a {
|
||||
|
||||
UnitBase::Second => $X!(
|
||||
|
||||
UnitBase::Second, // Repeat the name of this base unit
|
||||
"s", // String to display for this unit
|
||||
|
||||
// "base", "float", or "rational."
|
||||
// if base, this is a base unit and has no conversion factor.
|
||||
// if float or rational, this is not a base unit. See below.
|
||||
base
|
||||
),
|
||||
UnitBase::Meter => $X!(
|
||||
UnitBase::Meter, "m",
|
||||
base
|
||||
),
|
||||
UnitBase::Gram => $X!(
|
||||
UnitBase::Gram, "g",
|
||||
base
|
||||
),
|
||||
UnitBase::Ampere => $X!(
|
||||
UnitBase::Ampere, "a",
|
||||
base
|
||||
),
|
||||
UnitBase::Kelvin => $X!(
|
||||
UnitBase::Kelvin, "k",
|
||||
base
|
||||
),
|
||||
UnitBase::Mole => $X!(
|
||||
UnitBase::Mole, "mol",
|
||||
base
|
||||
),
|
||||
UnitBase::Candela => $X!(
|
||||
UnitBase::Candela, "c",
|
||||
base
|
||||
),
|
||||
|
||||
|
||||
|
||||
UnitBase::Minute => $X!(
|
||||
UnitBase::Minute, "min",
|
||||
|
||||
// "rational" and "float" determine what kind of Quantity
|
||||
// this unit's conversion factor will be. Use "rational"
|
||||
// if it is exact, and "float" if it is an approximation.
|
||||
rational,
|
||||
|
||||
|
||||
// The next two lines are interpreted as follows:
|
||||
// One Minute = 60 Seconds.
|
||||
|
||||
// The value
|
||||
"60",
|
||||
// The unit. Can be repeated for compound units.
|
||||
// MUST BE BASE UNITS.
|
||||
(UnitBase::Second, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Hour => $X!(
|
||||
UnitBase::Hour, "hour",
|
||||
rational, "3600",
|
||||
(UnitBase::Second, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Day => $X!(
|
||||
UnitBase::Day, "day",
|
||||
rational, "86400",
|
||||
(UnitBase::Second, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Foot => $X!(
|
||||
UnitBase::Foot, "ft",
|
||||
float, "0.3048",
|
||||
(UnitBase::Meter, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Inch => $X!(
|
||||
UnitBase::Inch, "in",
|
||||
float, "0.0254",
|
||||
(UnitBase::Meter, 1f64)
|
||||
),
|
||||
|
||||
UnitBase::Mile => $X!(
|
||||
UnitBase::Mile, "mile",
|
||||
float, "1609",
|
||||
(UnitBase::Meter, 1f64)
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
pub (self) use unit_db;
|
|
@ -75,6 +75,37 @@ impl Prefix {
|
|||
}
|
||||
|
||||
|
||||
|
||||
macro_rules! str_to_prefix {
|
||||
("") => {Prefix::None};
|
||||
("Q") => {Prefix::Quetta};
|
||||
("R") => {Prefix::Ronna};
|
||||
("Y") => {Prefix::Yotta};
|
||||
("Z") => {Prefix::Zetta};
|
||||
("E") => {Prefix::Exa};
|
||||
("P") => {Prefix::Peta};
|
||||
("T") => {Prefix::Tera};
|
||||
("G") => {Prefix::Giga};
|
||||
("M") => {Prefix::Mega};
|
||||
("k") => {Prefix::Kilo};
|
||||
("h") => {Prefix::Hecto};
|
||||
("da") => {Prefix::Deka};
|
||||
("d") => {Prefix::Deci};
|
||||
("c") => {Prefix::Centi};
|
||||
("m") => {Prefix::Milli};
|
||||
("u") => {Prefix::Micro};
|
||||
("n") => {Prefix::Nano};
|
||||
("p") => {Prefix::Pico};
|
||||
("f") => {Prefix::Femto};
|
||||
("a") => {Prefix::Atto};
|
||||
("z") => {Prefix::Zepto};
|
||||
("y") => {Prefix::Yocto};
|
||||
("r") => {Prefix::Ronto};
|
||||
("q") => {Prefix::Quecto};
|
||||
}
|
||||
pub (super) use str_to_prefix;
|
||||
|
||||
|
||||
impl ToString for Prefix {
|
||||
fn to_string(&self) -> String {
|
||||
String::from(match self {
|
||||
|
@ -107,4 +138,4 @@ impl ToString for Prefix {
|
|||
Prefix::None => ""
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,9 +6,11 @@ use std::ops::{
|
|||
|
||||
use crate::quantity::Scalar;
|
||||
use crate::quantity::Quantity;
|
||||
use super::FreeUnit;
|
||||
use super::UnitBase;
|
||||
use super::Prefix;
|
||||
use super::FreeUnit;
|
||||
use super::fromstring_db;
|
||||
use super::str_to_prefix;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone)]
|
||||
|
@ -74,6 +76,15 @@ impl Unit {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_free(f: FreeUnit) -> Unit {
|
||||
let mut u = Unit {
|
||||
val: HashMap::new()
|
||||
};
|
||||
|
||||
u.insert(f, Scalar::new_rational(1f64).unwrap());
|
||||
return u;
|
||||
}
|
||||
|
||||
pub fn get_val(&self) -> &HashMap<FreeUnit, Scalar> { &self.val }
|
||||
pub fn get_val_mut(&mut self) -> &mut HashMap<FreeUnit, Scalar> { &mut self.val }
|
||||
pub fn unitless(&self) -> bool { self.get_val().len() == 0 }
|
||||
|
@ -149,68 +160,6 @@ impl Unit {
|
|||
return u;
|
||||
}
|
||||
|
||||
|
||||
pub fn from_string(s: &str) -> Option<Quantity> {
|
||||
// Base Units
|
||||
let b = match s {
|
||||
"m" => Some(UnitBase::Meter),
|
||||
"s" => Some(UnitBase::Second),
|
||||
"sec" => Some(UnitBase::Second),
|
||||
"g" => Some(UnitBase::Gram),
|
||||
"a" => Some(UnitBase::Ampere),
|
||||
"k" => Some(UnitBase::Kelvin),
|
||||
"mol" => Some(UnitBase::Mole),
|
||||
"c" => Some(UnitBase::Candela),
|
||||
"ft" => Some(UnitBase::Foot),
|
||||
"mile" => Some(UnitBase::Mile),
|
||||
"hour" => Some(UnitBase::Hour),
|
||||
"min" => Some(UnitBase::Minute),
|
||||
"day" => Some(UnitBase::Day),
|
||||
_ => { None }
|
||||
};
|
||||
|
||||
if b.is_some() {
|
||||
let mut u = Unit::new();
|
||||
let b = FreeUnit::from_base(b.unwrap());
|
||||
|
||||
u.insert(b, Scalar::new_rational(1f64).unwrap());
|
||||
|
||||
let mut q = Quantity::new_rational(1f64).unwrap();
|
||||
q.set_unit(u);
|
||||
|
||||
return Some(q);
|
||||
};
|
||||
|
||||
if b.is_none() {
|
||||
if s == "kg" {
|
||||
let mut u = Unit::new();
|
||||
let b = FreeUnit::from_base_prefix(UnitBase::Gram, Prefix::Kilo);
|
||||
|
||||
u.insert(b, Scalar::new_rational(1f64).unwrap());
|
||||
|
||||
let mut q = Quantity::new_rational(1f64).unwrap();
|
||||
q.set_unit(u);
|
||||
|
||||
return Some(q);
|
||||
}
|
||||
|
||||
if s == "km" {
|
||||
let mut u = Unit::new();
|
||||
let b = FreeUnit::from_base_prefix(UnitBase::Meter, Prefix::Kilo);
|
||||
|
||||
u.insert(b, Scalar::new_rational(1f64).unwrap());
|
||||
|
||||
let mut q = Quantity::new_rational(1f64).unwrap();
|
||||
q.set_unit(u);
|
||||
|
||||
return Some(q);
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
pub fn to_base_factor(&self) -> Quantity {
|
||||
let mut q = Quantity::new_rational(1f64).unwrap();
|
||||
|
||||
|
@ -224,6 +173,55 @@ impl Unit {
|
|||
}
|
||||
|
||||
|
||||
|
||||
impl Unit {
|
||||
pub fn from_string(s: &str) -> Option<Quantity> {
|
||||
macro_rules! unpack_fromstring {
|
||||
(
|
||||
$(
|
||||
(
|
||||
$unit:expr,
|
||||
$string:literal
|
||||
$(, (
|
||||
$( $prefix:tt ),*
|
||||
))?
|
||||
)
|
||||
),*
|
||||
) => {
|
||||
// Build match statement for each unit and prefix
|
||||
match s {
|
||||
$(
|
||||
// No prefix--every unit has this
|
||||
$string => Some(FreeUnit::from_base($unit)),
|
||||
|
||||
// Arms for prefixes
|
||||
$($(
|
||||
concat!(
|
||||
$prefix,
|
||||
$string
|
||||
) => Some(FreeUnit::from_base_prefix($unit, str_to_prefix!($prefix))),
|
||||
)*)*
|
||||
)*
|
||||
_ => None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Big match statement
|
||||
let b = fromstring_db!(unpack_fromstring);
|
||||
|
||||
if b.is_none() { return None; }
|
||||
let b = Unit::from_free(b.unwrap());
|
||||
let mut q = Quantity::new_rational(1f64).unwrap();
|
||||
q.set_unit(b);
|
||||
return Some(q);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
impl PartialEq for Unit {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let v = self.get_val();
|
||||
|
|
Loading…
Reference in New Issue