Skip to main content

zerodds_idl_csharp/
type_map.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Mapping IDL-Primitive → C#-Type-Strings.
4//!
5//! Folgt OMG IDL4-CSharp-Mapping §6 (formal/2024-12-01) Tabelle 6-1.
6//! Foundation-Subset (Phase 3.2): primitive Skalare, Strings,
7//! sowie die Foundation-Container `IList<T>`, `T[]`, Discriminated-
8//! Union-Pattern, `T?` (Nullable).
9//!
10//! Spec-Mapping (vollstaendig fuer Phase 3.2):
11//!
12//! | IDL                  | C#       |
13//! |----------------------|----------|
14//! | `boolean`            | `bool`   |
15//! | `octet`              | `byte`   |
16//! | `char`               | `char`   |
17//! | `wchar`              | `char`   |
18//! | `short`              | `short`  |
19//! | `unsigned short`     | `ushort` |
20//! | `long`               | `int`    |
21//! | `unsigned long`      | `uint`   |
22//! | `long long`          | `long`   |
23//! | `unsigned long long` | `ulong`  |
24//! | `int8`               | `sbyte`  |
25//! | `uint8`              | `byte`   |
26//! | `float`              | `float`  |
27//! | `double`             | `double` |
28//! | `long double`        | `decimal` (Approx; spec erlaubt `decimal` oder `double`) |
29//! | `string`             | `string` |
30//! | `wstring`            | `string` |
31
32use zerodds_idl::ast::{FloatingType, IntegerType, PrimitiveType};
33
34/// Mappt eine [`PrimitiveType`] auf den C#-Typ-Ausdruck.
35#[must_use]
36pub fn primitive_to_cs(p: PrimitiveType) -> &'static str {
37    match p {
38        PrimitiveType::Boolean => "bool",
39        PrimitiveType::Octet => "byte",
40        PrimitiveType::Char => "char",
41        PrimitiveType::WideChar => "char",
42        PrimitiveType::Integer(i) => integer_to_cs(i),
43        PrimitiveType::Floating(f) => floating_to_cs(f),
44    }
45}
46
47/// Mapping fuer Integer-Subtypen (§6 Tabelle 6-1).
48#[must_use]
49pub fn integer_to_cs(i: IntegerType) -> &'static str {
50    match i {
51        IntegerType::Short | IntegerType::Int16 => "short",
52        IntegerType::Long | IntegerType::Int32 => "int",
53        IntegerType::LongLong | IntegerType::Int64 => "long",
54        IntegerType::UShort | IntegerType::UInt16 => "ushort",
55        IntegerType::ULong | IntegerType::UInt32 => "uint",
56        IntegerType::ULongLong | IntegerType::UInt64 => "ulong",
57        IntegerType::Int8 => "sbyte",
58        IntegerType::UInt8 => "byte",
59    }
60}
61
62/// Mapping fuer Floating-Subtypen.
63///
64/// `long double` wird in C# als `decimal` gemappt (Spec erlaubt
65/// platform-naehesten 80-bit/128-bit Type; `decimal` ist die naechste
66/// .NET-Approximation).
67#[must_use]
68pub fn floating_to_cs(f: FloatingType) -> &'static str {
69    match f {
70        FloatingType::Float => "float",
71        FloatingType::Double => "double",
72        FloatingType::LongDouble => "decimal",
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    #![allow(clippy::expect_used, clippy::panic)]
79    use super::*;
80
81    #[test]
82    fn primitive_boolean() {
83        assert_eq!(primitive_to_cs(PrimitiveType::Boolean), "bool");
84    }
85
86    #[test]
87    fn primitive_octet() {
88        assert_eq!(primitive_to_cs(PrimitiveType::Octet), "byte");
89    }
90
91    #[test]
92    fn primitive_char() {
93        assert_eq!(primitive_to_cs(PrimitiveType::Char), "char");
94    }
95
96    #[test]
97    fn primitive_wchar_collapses_to_char() {
98        assert_eq!(primitive_to_cs(PrimitiveType::WideChar), "char");
99    }
100
101    #[test]
102    fn integer_short_signed_unsigned() {
103        assert_eq!(integer_to_cs(IntegerType::Short), "short");
104        assert_eq!(integer_to_cs(IntegerType::UShort), "ushort");
105    }
106
107    #[test]
108    fn integer_long_signed_unsigned() {
109        assert_eq!(integer_to_cs(IntegerType::Long), "int");
110        assert_eq!(integer_to_cs(IntegerType::ULong), "uint");
111    }
112
113    #[test]
114    fn integer_long_long_signed_unsigned() {
115        assert_eq!(integer_to_cs(IntegerType::LongLong), "long");
116        assert_eq!(integer_to_cs(IntegerType::ULongLong), "ulong");
117    }
118
119    #[test]
120    fn integer_explicit_widths() {
121        assert_eq!(integer_to_cs(IntegerType::Int8), "sbyte");
122        assert_eq!(integer_to_cs(IntegerType::UInt8), "byte");
123        assert_eq!(integer_to_cs(IntegerType::Int16), "short");
124        assert_eq!(integer_to_cs(IntegerType::UInt16), "ushort");
125        assert_eq!(integer_to_cs(IntegerType::Int32), "int");
126        assert_eq!(integer_to_cs(IntegerType::UInt32), "uint");
127        assert_eq!(integer_to_cs(IntegerType::Int64), "long");
128        assert_eq!(integer_to_cs(IntegerType::UInt64), "ulong");
129    }
130
131    #[test]
132    fn floating_float_double() {
133        assert_eq!(floating_to_cs(FloatingType::Float), "float");
134        assert_eq!(floating_to_cs(FloatingType::Double), "double");
135    }
136
137    #[test]
138    fn floating_long_double_to_decimal() {
139        assert_eq!(floating_to_cs(FloatingType::LongDouble), "decimal");
140    }
141
142    #[test]
143    fn primitive_dispatches_through_integer() {
144        assert_eq!(
145            primitive_to_cs(PrimitiveType::Integer(IntegerType::Long)),
146            "int"
147        );
148        assert_eq!(
149            primitive_to_cs(PrimitiveType::Integer(IntegerType::ULongLong)),
150            "ulong"
151        );
152    }
153
154    #[test]
155    fn primitive_dispatches_through_floating() {
156        assert_eq!(
157            primitive_to_cs(PrimitiveType::Floating(FloatingType::Float)),
158            "float"
159        );
160    }
161
162    #[test]
163    fn all_14_primitives_have_distinct_or_intentional_mapping() {
164        // 14 Primitives laut Roadmap-Auftrag.
165        let mappings: Vec<(&str, &str)> = vec![
166            ("boolean", primitive_to_cs(PrimitiveType::Boolean)),
167            ("octet", primitive_to_cs(PrimitiveType::Octet)),
168            ("char", primitive_to_cs(PrimitiveType::Char)),
169            ("wchar", primitive_to_cs(PrimitiveType::WideChar)),
170            (
171                "short",
172                primitive_to_cs(PrimitiveType::Integer(IntegerType::Short)),
173            ),
174            (
175                "ushort",
176                primitive_to_cs(PrimitiveType::Integer(IntegerType::UShort)),
177            ),
178            (
179                "long",
180                primitive_to_cs(PrimitiveType::Integer(IntegerType::Long)),
181            ),
182            (
183                "ulong",
184                primitive_to_cs(PrimitiveType::Integer(IntegerType::ULong)),
185            ),
186            (
187                "long long",
188                primitive_to_cs(PrimitiveType::Integer(IntegerType::LongLong)),
189            ),
190            (
191                "ulong long",
192                primitive_to_cs(PrimitiveType::Integer(IntegerType::ULongLong)),
193            ),
194            (
195                "float",
196                primitive_to_cs(PrimitiveType::Floating(FloatingType::Float)),
197            ),
198            (
199                "double",
200                primitive_to_cs(PrimitiveType::Floating(FloatingType::Double)),
201            ),
202            (
203                "long double",
204                primitive_to_cs(PrimitiveType::Floating(FloatingType::LongDouble)),
205            ),
206        ];
207        // Mindestens 13 distinkte Strings (char/wchar kollabieren auf "char").
208        let unique: std::collections::BTreeSet<&str> = mappings.iter().map(|(_, cs)| *cs).collect();
209        assert!(
210            unique.len() >= 12,
211            "expected ≥12 distinct mappings, got {} ({:?})",
212            unique.len(),
213            unique
214        );
215    }
216}