1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! This is an auto-generated wrapper around the `om2:Unit` class, with a few
//! supporting structs/enums/utils.
//!
//! The idea here is to map Unit into a big (de)serializable rust enum, so you
//! don't need to know the label/symbol up front but can just grab it from the
//! Unit enum:
//!
//! ```rust
//! use om2::Unit;
//!
//! let wh = Unit::WattHour;
//! assert_eq!(wh.symbol(), Some("W h".to_string()));
//! assert_eq!(wh.label(), Some("watt hour".to_string()));
//! ```
//!
//! Note that the `symbol()` and `label()` methods return Option because not all
//! the enum values have those fields defined int he schema.
//!
//! This crate also exports `om2:Measure`, useful for holding full measurements.
//! It can be built easily with the builder pattern, and also has getters by
//! default, with the option to include setters as well:
//!
//! ```rust
//! use om2::{Unit, Measure, NumericUnion};
//! let measure = Measure::builder()
//!     .has_numerical_value(NumericUnion::Double(7.3))
//!     .has_unit(Unit::Hour)
//!     .build().unwrap();
//! assert_eq!(measure.has_unit(), &Unit::Hour);
//! assert_eq!(measure.has_numerical_value(), &NumericUnion::Double(7.3));
//! // turn the measure into a builder for cumbersome immutable updates
//! let measure2 = measure.into_builder()
//!     .has_unit(Unit::Parsec)
//!     .has_numerical_value(NumericUnion::Integer(12))
//!     .build().unwrap();
//! ```
//!
//! Note that none of the available RDF/XML rust libs were able to parse the om2
//! RDF schema, so this library requires conversion to .ttl first (this is done
//! via the `make schema` command, which uses the `rapper` command line util
//! under the hood).
//!
//! Features:
//!
//! - `into_builder` - (default) implements `.into_builder()` for provided
//! structs so existing structs can be modified via the builder pattern.
//! - `getset_setters` - implements setters on the generated structs so they can
//! be mutated in-place via setter methods
//! - `getset_getmut` - implements mutable getters on the generated structs so
//! they can be mutated in-place via &mut getters
//!
//! Note that *all* features are enabled when building the docs to give a sense
//! of the library's full abilities.

use derive_builder::Builder;
use getset::Getters;
#[cfg(feature = "getset_setters")]
use getset::Setters;
#[cfg(feature = "getset_getmut")]
use getset::MutGetters;
use rust_decimal::Decimal;
use serde_derive::{Serialize, Deserialize};

mod gen;

/// Supportive module containing NumericUnion (used mainly in the om2 structs)
mod dtype {
    use super::*;

    /// A datatype that is the union of numeric xsd data types. "numericUnion" is equivalent to the xsd specification that uses an xsd:union of memberTypes="xsd:decimal xsd:double xsd:float xsd:integer".
    ///
    /// ID: http://www.linkedmodel.org/schema/dtype#numericUnion
    #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
    pub enum NumericUnion {
        /// decimal represents a subset of the real numbers, which can be represented by decimal numerals.
        #[serde(rename = "decimal")]
        Decimal(Decimal),
        /// The double datatype is patterned after the IEEE double-precision 64-bit floating point type [IEEE 754-1985].
        #[serde(rename = "double")]
        Double(f64),
        /// float is patterned after the IEEE single-precision 32-bit floating point type [IEEE 754-1985].
        #[serde(rename = "float")]
        Float(f32),
        /// integer is ·derived· from decimal by fixing the value of ·fractionDigits· to be 0and disallowing the trailing decimal point.
        #[serde(rename = "integer")]
        Integer(i64),
    }
}

pub use gen::*;
pub use dtype::*;

/// A numeric value with its unit of measure.
///
/// ID: http://www.ontology-of-units-of-measure.org/resource/om-2/Measure
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Builder, Getters)]
#[cfg_attr(feature = "getset_setters", derive(Setters))]
#[cfg_attr(feature = "getset_getmut", derive(MutGetters))]
#[builder(pattern = "owned", setter(into))]
#[getset(get = "pub", set = "pub", get_mut = "pub")]
pub struct Measure {
    pub has_numerical_value: dtype::NumericUnion,
    pub has_unit: Unit,
}

impl Measure {
    pub fn builder() -> MeasureBuilder {
        MeasureBuilder {
            has_numerical_value: None,
            has_unit: None,
        }
    }

    pub fn into_builder(self) -> MeasureBuilder {
        let mut builder = Self::builder();
        builder = builder.has_numerical_value(self.has_numerical_value);
        builder = builder.has_unit(self.has_unit);
        builder
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn can_build() {
        let measure = Measure::builder()
            .has_numerical_value(NumericUnion::Double(42.0))
            .has_unit(Unit::WattHour)
            .build().unwrap();
        assert_eq!(measure.has_numerical_value(), &NumericUnion::Double(42.0));
        assert_eq!(measure.has_unit(), &Unit::WattHour);
    }

    #[cfg(feature = "into_builder")]
    #[test]
    fn into_builder() {
        let measure = Measure::builder()
            .has_numerical_value(NumericUnion::Double(42.0))
            .has_unit(Unit::WattHour)
            .build().unwrap();
        let measure2 = measure.into_builder()
            .has_unit(Unit::Hour)
            .build().unwrap();
        assert_eq!(measure2.has_numerical_value(), &NumericUnion::Double(42.0));
        assert_eq!(measure2.has_unit(), &Unit::Hour);
    }
}