Skip to main content

zerodds_types/dynamic/
type_.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! DynamicType + DynamicTypeMember (XTypes 1.3 §7.5.3).
4//!
5//! `DynamicType` ist eine Read-Only-Sicht auf einen zur Laufzeit
6//! konstruierten Typ. Konstruktion erfolgt via
7//! [`crate::dynamic::DynamicTypeBuilder`], danach ist die Instanz
8//! immutable.
9
10use alloc::string::String;
11use alloc::sync::Arc;
12use alloc::vec::Vec;
13
14use super::descriptor::{MemberDescriptor, MemberId, TypeDescriptor, TypeKind};
15use super::error::DynamicError;
16
17/// Member-Sicht auf einen DynamicType (Spec §7.5.3.4 GetMember).
18///
19/// Jeder Member kapselt seinen Descriptor + den ihm zugeordneten
20/// (rekursiven) DynamicType.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct DynamicTypeMember {
23    pub(super) descriptor: MemberDescriptor,
24    pub(super) member_type: DynamicType,
25}
26
27impl DynamicTypeMember {
28    /// Lese-Sicht auf den Descriptor.
29    #[must_use]
30    pub fn descriptor(&self) -> &MemberDescriptor {
31        &self.descriptor
32    }
33
34    /// Member-Name.
35    #[must_use]
36    pub fn name(&self) -> &str {
37        &self.descriptor.name
38    }
39
40    /// Member-Id.
41    #[must_use]
42    pub fn id(&self) -> MemberId {
43        self.descriptor.id
44    }
45
46    /// `index` im Composite (Reihenfolge).
47    #[must_use]
48    pub fn index(&self) -> u32 {
49        self.descriptor.index
50    }
51
52    /// Vollstaendiger DynamicType dieses Members.
53    #[must_use]
54    pub fn dynamic_type(&self) -> &DynamicType {
55        &self.member_type
56    }
57
58    /// `equals` per Spec §7.5.3.5: Deep-Equality.
59    #[must_use]
60    pub fn equals(&self, other: &Self) -> bool {
61        self.descriptor == other.descriptor && self.member_type.equals(&other.member_type)
62    }
63}
64
65/// Innerer Zustand eines DynamicType — referenz-gezaehlt fuer billiges
66/// Cloning. Gleicher Inhalt → strukturell gleich (PartialEq), aber ggf.
67/// anderer Arc-Pointer (Equality wird strukturell verglichen).
68#[derive(Debug)]
69pub(super) struct DynamicTypeInner {
70    pub(super) descriptor: TypeDescriptor,
71    pub(super) members: Vec<DynamicTypeMember>,
72}
73
74impl PartialEq for DynamicTypeInner {
75    fn eq(&self, other: &Self) -> bool {
76        self.descriptor == other.descriptor && self.members == other.members
77    }
78}
79
80impl Eq for DynamicTypeInner {}
81
82/// XTypes 1.3 §7.5.3 DynamicType.
83///
84/// Read-only-API auf einen zur Laufzeit konstruierten Type. Erzeugt
85/// werden DynamicTypes ausschliesslich via `DynamicTypeBuilder::build`
86/// oder `DynamicTypeBuilderFactory::get_primitive_type`.
87#[derive(Debug, Clone)]
88pub struct DynamicType {
89    pub(super) inner: Arc<DynamicTypeInner>,
90}
91
92impl PartialEq for DynamicType {
93    fn eq(&self, other: &Self) -> bool {
94        // Strukturelle Gleichheit (die `equals`-Spec-Methode); Arc-Identity
95        // ist nur eine Optimierung.
96        Arc::ptr_eq(&self.inner, &other.inner) || *self.inner == *other.inner
97    }
98}
99
100impl Eq for DynamicType {}
101
102impl DynamicType {
103    pub(super) fn from_inner(inner: DynamicTypeInner) -> Self {
104        Self {
105            inner: Arc::new(inner),
106        }
107    }
108
109    /// Spec §7.5.3.1.1 `get_name()`.
110    #[must_use]
111    pub fn name(&self) -> &str {
112        &self.inner.descriptor.name
113    }
114
115    /// Spec §7.5.3.1.2 `get_kind()`.
116    #[must_use]
117    pub fn kind(&self) -> TypeKind {
118        self.inner.descriptor.kind
119    }
120
121    /// Spec §7.5.3.1 `get_descriptor()`.
122    #[must_use]
123    pub fn descriptor(&self) -> &TypeDescriptor {
124        &self.inner.descriptor
125    }
126
127    /// Spec §7.5.3.4.1 `get_member_count()`.
128    #[must_use]
129    pub fn member_count(&self) -> u32 {
130        u32::try_from(self.inner.members.len()).unwrap_or(u32::MAX)
131    }
132
133    /// Spec §7.5.3.4.4 `get_member_by_index(index)`.
134    #[must_use]
135    pub fn member_by_index(&self, index: u32) -> Option<&DynamicTypeMember> {
136        self.inner.members.get(index as usize)
137    }
138
139    /// Spec §7.5.3.4.2 `get_member(MemberId)`.
140    #[must_use]
141    pub fn member_by_id(&self, id: MemberId) -> Option<&DynamicTypeMember> {
142        self.inner.members.iter().find(|m| m.descriptor.id == id)
143    }
144
145    /// Spec §7.5.3.4.3 `get_member_by_name(name)`.
146    #[must_use]
147    pub fn member_by_name(&self, name: &str) -> Option<&DynamicTypeMember> {
148        self.inner
149            .members
150            .iter()
151            .find(|m| m.descriptor.name == name)
152    }
153
154    /// Iterator ueber alle Members in Index-Reihenfolge.
155    pub fn members(&self) -> impl Iterator<Item = &DynamicTypeMember> {
156        self.inner.members.iter()
157    }
158
159    /// Spec §7.5.3.5 `equals(other)` — Deep-Equality.
160    #[must_use]
161    pub fn equals(&self, other: &Self) -> bool {
162        if Arc::ptr_eq(&self.inner, &other.inner) {
163            return true;
164        }
165        if self.inner.descriptor != other.inner.descriptor
166            || self.inner.members.len() != other.inner.members.len()
167        {
168            return false;
169        }
170        self.inner
171            .members
172            .iter()
173            .zip(other.inner.members.iter())
174            .all(|(a, b)| a.equals(b))
175    }
176
177    /// True wenn der Typ ein Composite-Type ist (Members traegt).
178    #[must_use]
179    pub fn is_aggregable(&self) -> bool {
180        self.kind().is_aggregable()
181    }
182
183    /// Validiert, dass der Typ insgesamt konsistent ist (Spec §7.5.3.5
184    /// + Block-A-Constraints + Member-Konsistenz).
185    ///
186    /// # Errors
187    /// `DynamicError::Inconsistent` mit Detail.
188    pub fn is_consistent(&self) -> Result<(), DynamicError> {
189        self.inner
190            .descriptor
191            .is_consistent()
192            .map_err(DynamicError::inconsistent)?;
193        for m in &self.inner.members {
194            m.descriptor
195                .is_consistent()
196                .map_err(DynamicError::inconsistent)?;
197        }
198        Ok(())
199    }
200
201    /// Convenience: erzeugt einen primitiven `DynamicType` fuer Type-Bridges.
202    ///
203    /// Fuer mehrfach-genutzte Primitive bevorzuge
204    /// [`crate::dynamic::DynamicTypeBuilderFactory::get_primitive_type`]
205    /// (Singleton-Cache).
206    #[must_use]
207    pub fn new_primitive(kind: TypeKind) -> Self {
208        let name = primitive_name(kind);
209        Self::from_inner(DynamicTypeInner {
210            descriptor: TypeDescriptor::primitive(kind, String::from(name)),
211            members: Vec::new(),
212        })
213    }
214}
215
216/// Kanonisches Spec-Mapping kind → Name (Spec §7.5.1 Table 10 + IDL-Spelling).
217pub(super) const fn primitive_name(kind: TypeKind) -> &'static str {
218    match kind {
219        TypeKind::Boolean => "boolean",
220        TypeKind::Byte => "octet",
221        TypeKind::Int8 => "int8",
222        TypeKind::UInt8 => "uint8",
223        TypeKind::Int16 => "int16",
224        TypeKind::UInt16 => "uint16",
225        TypeKind::Int32 => "int32",
226        TypeKind::UInt32 => "uint32",
227        TypeKind::Int64 => "int64",
228        TypeKind::UInt64 => "uint64",
229        TypeKind::Float32 => "float",
230        TypeKind::Float64 => "double",
231        TypeKind::Float128 => "long double",
232        TypeKind::Char8 => "char",
233        TypeKind::Char16 => "wchar",
234        _ => "<non-primitive>",
235    }
236}
237
238#[cfg(test)]
239#[allow(clippy::unwrap_used)]
240mod tests {
241    use super::*;
242
243    #[test]
244    fn primitive_dynamic_type_has_correct_kind_and_name() {
245        let t = DynamicType::new_primitive(TypeKind::Int32);
246        assert_eq!(t.kind(), TypeKind::Int32);
247        assert_eq!(t.name(), "int32");
248        assert_eq!(t.member_count(), 0);
249    }
250
251    #[test]
252    fn equals_is_reflexive_and_value_based() {
253        let a = DynamicType::new_primitive(TypeKind::Int32);
254        let b = DynamicType::new_primitive(TypeKind::Int32);
255        assert!(a.equals(&a));
256        // verschiedene Arcs, aber strukturell gleich:
257        assert!(a.equals(&b));
258        assert_eq!(a, b);
259    }
260
261    #[test]
262    fn equals_distinguishes_different_kinds() {
263        let a = DynamicType::new_primitive(TypeKind::Int32);
264        let b = DynamicType::new_primitive(TypeKind::Int64);
265        assert!(!a.equals(&b));
266    }
267}