Skip to main content

bo4e_core/com/
address.rs

1//! Address (Adresse) component.
2
3use serde::{Deserialize, Serialize};
4
5use crate::enums::Country;
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8/// Physical or postal address.
9///
10/// German: Adresse
11///
12/// # Example
13///
14/// ```rust
15/// use bo4e_core::com::Address;
16///
17/// let address = Address {
18///     street: Some("Musterstraße".to_string()),
19///     house_number: Some("42".to_string()),
20///     postal_code: Some("50667".to_string()),
21///     city: Some("Köln".to_string()),
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 = "Adresse"))]
28#[serde(rename_all = "camelCase")]
29pub struct Address {
30    /// BO4E metadata
31    #[serde(flatten)]
32    pub meta: Bo4eMeta,
33
34    /// Street name (Strasse)
35    #[serde(skip_serializing_if = "Option::is_none", alias = "strasse")]
36    #[cfg_attr(feature = "json-schema", schemars(rename = "strasse"))]
37    pub street: Option<String>,
38
39    /// House number (Hausnummer)
40    #[serde(skip_serializing_if = "Option::is_none", alias = "hausnummer")]
41    #[cfg_attr(feature = "json-schema", schemars(rename = "hausnummer"))]
42    pub house_number: Option<String>,
43
44    /// Postal code (Postleitzahl)
45    #[serde(skip_serializing_if = "Option::is_none", alias = "postleitzahl")]
46    #[cfg_attr(feature = "json-schema", schemars(rename = "postleitzahl"))]
47    pub postal_code: Option<String>,
48
49    /// City/town (Ort)
50    #[serde(skip_serializing_if = "Option::is_none", alias = "ort")]
51    #[cfg_attr(feature = "json-schema", schemars(rename = "ort"))]
52    pub city: Option<String>,
53
54    /// District (Ortsteil)
55    #[serde(skip_serializing_if = "Option::is_none", alias = "ortsteil")]
56    #[cfg_attr(feature = "json-schema", schemars(rename = "ortsteil"))]
57    pub district: Option<String>,
58
59    /// PO Box number (Postfach)
60    #[serde(skip_serializing_if = "Option::is_none", alias = "postfach")]
61    #[cfg_attr(feature = "json-schema", schemars(rename = "postfach"))]
62    pub po_box: Option<String>,
63
64    /// Address addition/note (Adresszusatz)
65    #[serde(skip_serializing_if = "Option::is_none", alias = "adresszusatz")]
66    #[cfg_attr(feature = "json-schema", schemars(rename = "adresszusatz"))]
67    pub address_addition: Option<String>,
68
69    /// Co-location info - c/o address (CoErgaenzung)
70    #[serde(skip_serializing_if = "Option::is_none", alias = "coErgaenzung")]
71    #[cfg_attr(feature = "json-schema", schemars(rename = "coErgaenzung"))]
72    pub co_ergaenzung: Option<String>,
73
74    /// Country code (Landescode)
75    #[serde(skip_serializing_if = "Option::is_none", alias = "landescode")]
76    #[cfg_attr(feature = "json-schema", schemars(rename = "landescode"))]
77    pub country_code: Option<Country>,
78}
79
80impl Bo4eObject for Address {
81    fn type_name_german() -> &'static str {
82        "Adresse"
83    }
84
85    fn type_name_english() -> &'static str {
86        "Address"
87    }
88
89    fn meta(&self) -> &Bo4eMeta {
90        &self.meta
91    }
92
93    fn meta_mut(&mut self) -> &mut Bo4eMeta {
94        &mut self.meta
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_address_default() {
104        let address = Address::default();
105        assert!(address.street.is_none());
106        assert!(address.city.is_none());
107    }
108
109    #[test]
110    fn test_address_serialize() {
111        let address = Address {
112            street: Some("Hauptstraße".to_string()),
113            house_number: Some("1".to_string()),
114            postal_code: Some("12345".to_string()),
115            city: Some("Berlin".to_string()),
116            ..Default::default()
117        };
118
119        let json = serde_json::to_string(&address).unwrap();
120        assert!(json.contains(r#""street":"Hauptstraße""#));
121        assert!(json.contains(r#""houseNumber":"1""#));
122        assert!(json.contains(r#""postalCode":"12345""#));
123        assert!(json.contains(r#""city":"Berlin""#));
124    }
125
126    #[test]
127    fn test_address_deserialize() {
128        let json = r#"{
129            "street": "Musterweg",
130            "houseNumber": "42a",
131            "postalCode": "50667",
132            "city": "Köln"
133        }"#;
134
135        let address: Address = serde_json::from_str(json).unwrap();
136        assert_eq!(address.street, Some("Musterweg".to_string()));
137        assert_eq!(address.house_number, Some("42a".to_string()));
138        assert_eq!(address.postal_code, Some("50667".to_string()));
139        assert_eq!(address.city, Some("Köln".to_string()));
140    }
141
142    #[test]
143    fn test_address_roundtrip() {
144        let address = Address {
145            meta: Bo4eMeta::with_type("Adresse"),
146            street: Some("Teststraße".to_string()),
147            house_number: Some("123".to_string()),
148            postal_code: Some("99999".to_string()),
149            city: Some("Teststadt".to_string()),
150            ..Default::default()
151        };
152
153        let json = serde_json::to_string(&address).unwrap();
154        let parsed: Address = serde_json::from_str(&json).unwrap();
155        assert_eq!(address, parsed);
156    }
157
158    #[test]
159    fn test_address_with_country() {
160        let address = Address {
161            street: Some("Musterstraße".to_string()),
162            city: Some("München".to_string()),
163            country_code: Some(Country::Germany),
164            ..Default::default()
165        };
166
167        let json = serde_json::to_string(&address).unwrap();
168        assert!(json.contains(r#""countryCode":"DE""#));
169
170        let parsed: Address = serde_json::from_str(&json).unwrap();
171        assert_eq!(parsed.country_code, Some(Country::Germany));
172    }
173
174    #[test]
175    fn test_bo4e_object_impl() {
176        assert_eq!(Address::type_name_german(), "Adresse");
177        assert_eq!(Address::type_name_english(), "Address");
178    }
179}