iliekturtles / uom

Units of measurement -- type-safe zero-cost dimensional analysis

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

deserialize values to user-specified unit type

robfitzgerald opened this issue · comments

hi there! thanks for your work.

i want to use uom in code that reads CSVs of velocity data from a user and builds lookup functions from it. i would like to allow the user to specify their velocity unit, but via system configuration instead of inline with every CSV cell, i.e.:

pub fb build_lookup(filename: String, velocity_type: ?) -> {
  ...
  for row in file {
    let vel: Velocity<?> = Velocity::try_from(velocity_type, row.velocity)?;
    ...
  } 
} 

i didn't find any documentation on use of serde with this package to show me how the Type might be parameterized at deserialization, and i did find issues that imply that Units aren't actually reified into enums or structs here to allow matching. i didn't find a function to do this automagically. i tried to build my own enums, which means i have to duplicate the list of supported velocity types, but then i ran into the problem that the return type of my as_quantity method below would need to return a specific type argument:

#[derive(Debug, Serialize, Deserialize)]
pub enum VelocityType {
    MetersPerSecond,
    KilometersPerHour,
    MilesPerHour,
}

impl VelocityType {
    pub fn as_quantity(&self, value: f64) -> Velocity<?> {
      match self {
        MetersPerSecond -> Velocity::new<meters_per_second>(value),
        ...
      }
    }
}

wondering if i missed a way to get this kind of behavior, or if using uom i need to 1) require all users specify velocity in one type such as Velocity<kilometers_per_hour>, 2) require users to append the unit type to every velocity entry in the CSV ("40.2 kph", "41.52 kph" for 55 million rows).

thank you for reading!

You're right that uom doesn't include units during (de)serialization and because of the generic requirements on fn new constructing a quantity with a run-time provided unit is not ideal right now. Long term I want to address this issue. Short-term I see a few options, none which are very ideal.

  • Include the unit in your file as you mentioned. That's a lot of wasted bytes, but likely the easiest solution.
  • Concatenate the configuration-supplied unit with the value before parsing. That's lots of string concatenation, but you save file size.
  • Write a custom parsing function similar to the from_str impl where the unit is provided as a parameter. You would need to manually write out the match expression but would get the best performance/smallest file.

uom/src/quantity.rs

Lines 434 to 445 in 787272d

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.splitn(2, ' ');
let value = parts.next().unwrap();
let unit = parts.next().ok_or(NoSeparator)?;
let value = value.parse::<V>().map_err(|_| ValueParseError)?;
#[allow(unreachable_patterns)]
match unit.trim() {
$($abbreviation | $singular | $plural => Ok(Self::new::<super::super::$unit>(value)),)+
_ => Err(UnknownUnit),
}
}