Skip to main content

egml_core/model/base/
abstract_gml.rs

1use crate::model::abstract_object::{AbstractObject, AsAbstractObject, AsAbstractObjectMut};
2use crate::model::base::Id;
3use crate::model::basic_types::Code;
4
5/// Base data shared by every GML object ([OGC 07-036 ยง7.2.2.2](https://docs.ogc.org/is/07-036/07-036.pdf), `gml:AbstractGMLType`).
6///
7/// Every GML object carries an optional stable [`Id`] and zero-or-more human-readable
8/// name strings.  Concrete geometry and feature types embed `AbstractGml` and
9/// expose it through the [`AsAbstractGml`] trait.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
11pub struct AbstractGml {
12    pub abstract_object: AbstractObject,
13    /// Optional stable identifier for this GML object.
14    id: Option<Id>,
15    /// Human-readable names associated with this GML object.
16    ///
17    /// Stored as a boxed slice: names are rarely present and rarely modified,
18    /// so dropping `Vec`'s capacity field saves 8 bytes on every GML object.
19    names: Box<[Code]>,
20}
21
22impl AbstractGml {
23    /// Creates a new `AbstractGml` with no id and no names.
24    ///
25    /// # Examples
26    ///
27    /// ```rust
28    /// use egml_core::model::base::AbstractGml;
29    /// use crate::egml_core::model::base::AsAbstractGml;
30    ///
31    /// let gml = AbstractGml::new();
32    /// assert!(gml.id().is_none());
33    /// assert!(gml.names().is_empty());
34    /// ```
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    pub fn from_abstract_object(abstract_object: AbstractObject) -> Self {
40        Self {
41            abstract_object,
42            id: None,
43            names: Box::default(),
44        }
45    }
46
47    /// Creates a new `AbstractGml` pre-populated with the given `id`.
48    ///
49    /// # Examples
50    ///
51    /// ```rust
52    /// use egml_core::model::base::{AbstractGml, Id};
53    /// use crate::egml_core::model::base::AsAbstractGml;
54    ///
55    /// let id = Id::from_hashed_string("my-object");
56    /// let gml = AbstractGml::with_id(id);
57    /// assert!(gml.id().is_some());
58    /// ```
59    pub fn with_id(id: Id) -> Self {
60        Self {
61            id: Some(id),
62            ..Default::default()
63        }
64    }
65
66    /// Creates a new `AbstractGml` with an optional id.
67    ///
68    /// Equivalent to [`with_id`](Self::with_id) when `id` is `Some`, and to
69    /// [`new`](Self::new) when `id` is `None`.
70    pub fn with_optional_id(id: Option<Id>) -> Self {
71        Self {
72            id,
73            ..Default::default()
74        }
75    }
76}
77
78/// Object-safe read accessor for [`AbstractGml`] fields.
79///
80/// Implemented by all GML object types.  The default methods delegate to
81/// [`abstract_gml()`](Self::abstract_gml), so implementors only need to
82/// provide that single method.
83pub trait AsAbstractGml: AsAbstractObject {
84    /// Returns a reference to the embedded [`AbstractGml`] base data.
85    fn abstract_gml(&self) -> &AbstractGml;
86
87    /// Returns the optional identifier of this GML object.
88    fn id(&self) -> Option<&Id> {
89        self.abstract_gml().id.as_ref()
90    }
91
92    /// Returns the names of this GML object.
93    fn names(&self) -> &[Code] {
94        &self.abstract_gml().names
95    }
96}
97
98/// Mutable companion to [`AsAbstractGml`].
99///
100/// Implemented by all GML object types that expose mutable access to their
101/// base data.
102pub trait AsAbstractGmlMut: AsAbstractObjectMut + AsAbstractGml {
103    /// Returns a mutable reference to the embedded [`AbstractGml`] base data.
104    fn abstract_gml_mut(&mut self) -> &mut AbstractGml;
105
106    /// Sets the identifier of this GML object.
107    fn set_id(&mut self, id: Id) {
108        self.abstract_gml_mut().id = Some(id);
109    }
110
111    /// Sets or clears the identifier of this GML object.
112    fn set_id_opt(&mut self, id: Option<Id>) {
113        self.abstract_gml_mut().id = id;
114    }
115
116    /// Clears the identifier of this GML object.
117    fn clear_id(&mut self) {
118        self.abstract_gml_mut().id = None;
119    }
120
121    fn set_names(&mut self, names: Vec<Code>) {
122        self.abstract_gml_mut().names = names.into_boxed_slice();
123    }
124
125    /// Appends a name. Reallocates the name list on every call.
126    fn push_name(&mut self, name: Code) {
127        self.extend_names([name]);
128    }
129
130    /// Appends names. Reallocates the name list once per call.
131    fn extend_names(&mut self, names: impl IntoIterator<Item = Code>) {
132        let current = std::mem::take(&mut self.abstract_gml_mut().names);
133        self.abstract_gml_mut().names = current.into_vec().into_iter().chain(names).collect();
134    }
135}
136
137impl AsAbstractGml for AbstractGml {
138    fn abstract_gml(&self) -> &AbstractGml {
139        self
140    }
141}
142
143impl AsAbstractGmlMut for AbstractGml {
144    fn abstract_gml_mut(&mut self) -> &mut AbstractGml {
145        self
146    }
147}
148
149#[macro_export]
150macro_rules! impl_abstract_gml_traits {
151    ($type:ty) => {
152        impl $crate::model::AsAbstractObject for $type {
153            fn abstract_object(&self) -> &$crate::model::AbstractObject {
154                &<$type as $crate::model::base::AsAbstractGml>::abstract_gml(self).abstract_object
155            }
156        }
157    };
158}
159
160#[macro_export]
161macro_rules! impl_abstract_gml_mut_traits {
162    ($type:ty) => {
163        impl $crate::model::AsAbstractObjectMut for $type {
164            fn abstract_object_mut(&mut self) -> &mut $crate::model::AbstractObject {
165                &mut <$type as $crate::model::base::AsAbstractGmlMut>::abstract_gml_mut(self)
166                    .abstract_object
167            }
168        }
169    };
170}
171
172impl_abstract_gml_traits!(AbstractGml);
173impl_abstract_gml_mut_traits!(AbstractGml);
174
175#[cfg(test)]
176mod tests {
177    use super::{AbstractGml, AsAbstractGml, AsAbstractGmlMut};
178    use crate::model::basic_types::Code;
179
180    #[test]
181    fn push_name_appends_in_order() {
182        let mut gml = AbstractGml::new();
183        gml.push_name("a".into());
184        gml.push_name("b".into());
185        assert_eq!(gml.names(), [Code::from("a"), Code::from("b")]);
186    }
187
188    #[test]
189    fn extend_names_keeps_existing_names() {
190        let mut gml = AbstractGml::new();
191        gml.set_names(vec!["a".into()]);
192        gml.extend_names(["b".into(), "c".into()]);
193        assert_eq!(
194            gml.names(),
195            [Code::from("a"), Code::from("b"), Code::from("c")]
196        );
197    }
198}