Skip to content

Commit

Permalink
Add display tools for quantities.
Browse files Browse the repository at this point in the history
Resolves #13.
  • Loading branch information
Aehmlo authored and iliekturtles committed Nov 14, 2018
1 parent f81c40c commit 337eb9f
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/lib.rs
Expand Up @@ -548,6 +548,20 @@ storage_types! {
}
}

/// Utilities for formatting and printing quantities.
pub mod fmt {
/// An enum to specify the display style to use.
#[derive(Clone, Copy, Debug)]
pub enum DisplayStyle {
/// Display the value and a unit abbreviation, e.g. "1.0 m", "327 s".
Abbreviation,

/// Display the value and full unit name (pluralized as appropriate),
/// e.g. "1 kilogram", "756 feet".
Description,
}
}

/// Unicode string slice manipulation for quantities.
pub mod str {
/// Represents an error encountered while parsing a string into a `Quantity`.
Expand Down
96 changes: 96 additions & 0 deletions src/quantity.rs
Expand Up @@ -352,6 +352,102 @@ macro_rules! quantity {
{
Self::new::<N>(self.get::<N>().fract())
}

/// Creates a struct that can be used to format a compatible quantity for display.
///
/// # Notes
/// The return value of this method cannot be used to print directly, but is instead
/// used to format quantities and can be reused; see
/// [Arguments::with](../fmt/struct.Arguments.html#method.with) and the examples below.
///
/// If you do not need to format multiple quantities, consider using
/// [`to_format_args`](#method.to_format_args) instead.
///
/// # Examples
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::time::{femtosecond, picosecond};
/// # use uom::si::fmt::Arguments;
/// # use uom::fmt::DisplayStyle::*;
/// let t = Time::new::<picosecond>(1.0_E-1);
/// let a = Time::format_args(femtosecond, Description);
///
/// assert_eq!("100 femtoseconds", format!("{}", a.with(t)));
/// ```
pub fn format_args<N>(
unit: N,
style: $crate::fmt::DisplayStyle
) -> super::fmt::Arguments<Dimension, N>
where
N: Unit
{
super::fmt::Arguments {
dimension: $crate::lib::marker::PhantomData,
unit: unit,
style: style,
}
}

/// Creates a struct that formats `self` for display.
///
/// # Notes
/// Unlike [`format_args`](#method.format_args), the return value of this method *can*
/// be used directly for display. It will format the (cloned) value of `self` for the
/// quantity on which it is called **and nothing else**.
///
/// If you wish to reuse the return value to format multiple quantities, use
/// [`format_args`](#method.format_args) instead.
///
/// # Examples
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::time::{femtosecond, picosecond};
/// # use uom::si::fmt::Arguments;
/// # use uom::fmt::DisplayStyle::*;
/// let t = Time::new::<picosecond>(1.0_E-1);
/// let a = t.to_format_args(femtosecond, Description);
///
/// assert_eq!("100 femtoseconds", format!("{}", a));
/// ```
pub fn to_format_args<N>(
self,
unit: N,
style: $crate::fmt::DisplayStyle
) -> super::fmt::QuantityArguments<Dimension, U, V, N>
where
N: Unit
{
super::fmt::QuantityArguments {
arguments: super::fmt::Arguments {
dimension: $crate::lib::marker::PhantomData,
unit: unit,
style: style,
},
quantity: self,
}
}
}

impl<N> super::fmt::Arguments<Dimension, N>
where
N: super::Unit + Unit,
{
/// Specifies a quantity to display.
pub fn with<U, V>(
self,
quantity: $quantity<U, V>
) -> super::fmt::QuantityArguments<Dimension, U, V, N>
where
U: super::Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V>,
{
super::fmt::QuantityArguments {
arguments: self,
quantity: quantity,
}
}
}

mod str {
Expand Down
153 changes: 152 additions & 1 deletion src/system.rs
Expand Up @@ -175,7 +175,7 @@ macro_rules! system {
/// # use uom::si::f32::*;
/// # use uom::si::length::meter;
/// // Create a length of 1 meter.
/// let L = Length::new::<meter>(1.0);
/// let l = Length::new::<meter>(1.0);
/// ```
///
/// `Quantity` fields are public to allow for the creation of `const` values and instances
Expand Down Expand Up @@ -1230,6 +1230,157 @@ macro_rules! system {
}
}}

/// Utilities for formatting and printing quantities.
pub mod fmt {
use $crate::lib::fmt;
use super::{Dimension, Quantity, Unit, Units, from_base};
use $crate::num::Num;
use $crate::Conversion;
use $crate::fmt::DisplayStyle;

/// A struct to specify a display style and unit.
///
/// # Usage
/// ## Indirect style
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::length::{centimeter, meter};
/// # use uom::si::fmt::Arguments;
/// # use uom::fmt::DisplayStyle::*;
/// let l = Length::new::<meter>(1.0);
/// let a = Length::format_args(centimeter, Description);
///
/// assert_eq!("100 centimeters", format!("{}", a.with(l)));
/// ```
///
/// ## Direct style
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::length::{centimeter, meter};
/// # use uom::si::fmt::Arguments;
/// # use uom::fmt::DisplayStyle::*;
/// let l = Length::new::<meter>(1.0);
/// let a = l.to_format_args(centimeter, Description);
///
/// assert_eq!("100 centimeters", format!("{}", a));
/// ```
#[allow(missing_debug_implementations)] // Prevent accidental direct use.
pub struct Arguments<D, N>
where
D: Dimension + ?Sized,
N: Unit,
{
pub(super) dimension: $crate::lib::marker::PhantomData<D>,
pub(super) unit: N,
pub(super) style: DisplayStyle,
}

impl<D, N> $crate::lib::clone::Clone for Arguments<D, N>
where
D: Dimension + ?Sized,
N: Unit,
{
fn clone(&self) -> Self {
Self {
dimension: $crate::lib::marker::PhantomData,
unit: self.unit.clone(),
style: self.style.clone()
}
}
}

impl<D, N> $crate::lib::marker::Copy for Arguments<D, N>
where
D: Dimension + ?Sized,
N: Unit,
{
}

/// A struct to specify a display style and unit for a given quantity.
///
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::length::{centimeter, meter};
/// # use uom::si::fmt::Arguments;
/// # use uom::fmt::DisplayStyle::*;
/// let l = Length::new::<meter>(1.0);
/// let a = l.to_format_args(centimeter, Description);
///
/// assert_eq!("100 centimeters", format!("{}", a));
/// ```
pub struct QuantityArguments<D, U, V, N>
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: Num + Conversion<V>,
N: Unit,
{
pub(super) arguments: Arguments<D, N>,
pub(super) quantity: Quantity<D, U, V>,
}

impl<D, U, V, N> $crate::lib::clone::Clone for QuantityArguments<D, U, V, N>
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V> + $crate::lib::clone::Clone,
N: Unit,
{
fn clone(&self) -> Self {
Self {
arguments: self.arguments.clone(),
quantity: self.quantity.clone()
}
}
}

impl<D, U, V, N> $crate::lib::marker::Copy for QuantityArguments<D, U, V, N>
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V> + $crate::lib::marker::Copy,
N: Unit,
{
}

macro_rules! format_arguments {
($style:ident) => {
impl<D, U, V, N> fmt::$style for QuantityArguments<D, U, V, N>
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: Num + Conversion<V> + fmt::$style,
N: Unit + Conversion<V, T = V::T>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = from_base::<D, U, V, N>(&self.quantity.value);

value.fmt(f)?;
write!(f, " {}",
match self.arguments.style {
DisplayStyle::Abbreviation => N::abbreviation(),
DisplayStyle::Description => {
if value.is_one() { N::singular() } else { N::plural() }
},
})
}
}
};
}

format_arguments!(Binary);
format_arguments!(Debug);
format_arguments!(Display);
format_arguments!(LowerExp);
format_arguments!(LowerHex);
format_arguments!(Octal);
format_arguments!(UpperExp);
format_arguments!(UpperHex);
}

/// Macro to implement [`quantity`](si/struct.Quantity.html) type aliases for a specific
/// [system of units][units] and value storage type.
///
Expand Down
13 changes: 13 additions & 0 deletions src/tests/asserts.rs
@@ -1,12 +1,21 @@
//! Static assertions.

use tests::*;

assert_impl!(arguments; Arguments<Q<Z0, Z0, Z0>, meter>,
Clone, Copy);
assert_impl!(display_style; ::fmt::DisplayStyle,
Clone, Copy);

storage_types! {
types: Float;

use tests::*;

assert_impl!(q; Quantity<Q<Z0, Z0, Z0>, U<V>, V>,
Clone, Copy, PartialEq, PartialOrd, Send, Sync);
assert_impl!(quantity_arguments; QuantityArguments<Q<Z0, Z0, Z0>, U<V>, V, meter>,
Clone, Copy);
}

storage_types! {
Expand All @@ -16,6 +25,8 @@ storage_types! {

assert_impl!(q; Quantity<Q<Z0, Z0, Z0>, U<V>, V>,
Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Send, Sync, ::lib::hash::Hash);
assert_impl!(quantity_arguments; QuantityArguments<Q<Z0, Z0, Z0>, U<V>, V, meter>,
Clone, Copy);
}

storage_types! {
Expand All @@ -25,4 +36,6 @@ storage_types! {

assert_impl!(q; Quantity<Q<Z0, Z0, Z0>, U<V>, V>,
Clone, Eq, Ord, PartialEq, PartialOrd, Send, Sync, ::lib::hash::Hash);
assert_impl!(quantity_arguments; QuantityArguments<Q<Z0, Z0, Z0>, U<V>, V, meter>,
Clone, Copy);
}
1 change: 1 addition & 0 deletions src/tests/mod.rs
@@ -1,5 +1,6 @@
//! Tests for `uom` macros.

use self::fmt::{Arguments, QuantityArguments};
use self::length::{kilometer, meter};
use self::mass::kilogram;
use self::thermodynamic_temperature::{degree_fahrenheit, kelvin};
Expand Down

0 comments on commit 337eb9f

Please sign in to comment.