Skip to main content

bo4e_core/com/
price.rs

1//! Price (Preis) component.
2
3use serde::{Deserialize, Serialize};
4
5use crate::enums::{Currency, PriceStatus, PriceType, Unit};
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8/// A price with value, currency, and unit.
9///
10/// German: Preis
11///
12/// # Example
13///
14/// ```rust
15/// use bo4e_core::com::Price;
16/// use bo4e_core::enums::{Currency, Unit};
17///
18/// let price = Price {
19///     value: Some(0.25),
20///     currency: Some(Currency::Eur),
21///     reference_unit: Some(Unit::KilowattHour),
22///     ..Default::default()
23/// };
24/// ```
25#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
26#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
27#[cfg_attr(feature = "json-schema", schemars(rename = "Preis"))]
28#[serde(rename_all = "camelCase")]
29pub struct Price {
30    /// BO4E metadata
31    #[serde(flatten)]
32    pub meta: Bo4eMeta,
33
34    /// Price value (Wert)
35    #[serde(skip_serializing_if = "Option::is_none")]
36    #[cfg_attr(feature = "json-schema", schemars(rename = "wert"))]
37    pub value: Option<f64>,
38
39    /// Currency (Waehrung)
40    #[serde(skip_serializing_if = "Option::is_none")]
41    #[cfg_attr(feature = "json-schema", schemars(rename = "waehrung"))]
42    pub currency: Option<Currency>,
43
44    /// Unit that the price applies to (Bezugswert)
45    #[serde(skip_serializing_if = "Option::is_none")]
46    #[cfg_attr(feature = "json-schema", schemars(rename = "bezugswert"))]
47    pub reference_unit: Option<Unit>,
48
49    /// Type of price (Preistyp)
50    #[serde(skip_serializing_if = "Option::is_none")]
51    #[cfg_attr(feature = "json-schema", schemars(rename = "preistyp"))]
52    pub price_type: Option<PriceType>,
53
54    /// Status of the price (Preisstatus)
55    #[serde(skip_serializing_if = "Option::is_none")]
56    #[cfg_attr(feature = "json-schema", schemars(rename = "preisstatus"))]
57    pub status: Option<PriceStatus>,
58}
59
60impl Bo4eObject for Price {
61    fn type_name_german() -> &'static str {
62        "Preis"
63    }
64
65    fn type_name_english() -> &'static str {
66        "Price"
67    }
68
69    fn meta(&self) -> &Bo4eMeta {
70        &self.meta
71    }
72
73    fn meta_mut(&mut self) -> &mut Bo4eMeta {
74        &mut self.meta
75    }
76}
77
78impl Price {
79    /// Create a price in EUR per kWh.
80    pub fn eur_per_kwh(value: f64) -> Self {
81        Self {
82            value: Some(value),
83            currency: Some(Currency::Eur),
84            reference_unit: Some(Unit::KilowattHour),
85            ..Default::default()
86        }
87    }
88
89    /// Create a price in EUR per month (base price).
90    pub fn eur_per_month(value: f64) -> Self {
91        Self {
92            value: Some(value),
93            currency: Some(Currency::Eur),
94            reference_unit: Some(Unit::Month),
95            price_type: Some(PriceType::BasePrice),
96            ..Default::default()
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_energy_price() {
107        let price = Price::eur_per_kwh(0.30);
108        assert_eq!(price.value, Some(0.30));
109        assert_eq!(price.currency, Some(Currency::Eur));
110        assert_eq!(price.reference_unit, Some(Unit::KilowattHour));
111    }
112
113    #[test]
114    fn test_base_price() {
115        let price = Price::eur_per_month(12.50);
116        assert_eq!(price.value, Some(12.50));
117        assert_eq!(price.currency, Some(Currency::Eur));
118        assert_eq!(price.reference_unit, Some(Unit::Month));
119        assert_eq!(price.price_type, Some(PriceType::BasePrice));
120    }
121
122    #[test]
123    fn test_serialize() {
124        let price = Price {
125            value: Some(12.50),
126            currency: Some(Currency::Eur),
127            ..Default::default()
128        };
129
130        let json = serde_json::to_string(&price).unwrap();
131        assert!(json.contains(r#""value":12.5"#));
132        assert!(json.contains(r#""currency":"EUR""#));
133    }
134
135    #[test]
136    fn test_roundtrip() {
137        let price = Price::eur_per_kwh(0.2567);
138        let json = serde_json::to_string(&price).unwrap();
139        let parsed: Price = serde_json::from_str(&json).unwrap();
140        assert_eq!(price, parsed);
141    }
142
143    #[test]
144    fn test_bo4e_object_impl() {
145        assert_eq!(Price::type_name_german(), "Preis");
146        assert_eq!(Price::type_name_english(), "Price");
147    }
148}