1use zerodds_idl::ast::{FloatingType, IntegerType, PrimitiveType};
11
12use crate::error::CppGenError;
13
14pub(crate) const CPP_RESERVED: &[&str] = &[
21 "alignas",
22 "alignof",
23 "and",
24 "and_eq",
25 "asm",
26 "auto",
27 "bitand",
28 "bitor",
29 "bool",
30 "break",
31 "case",
32 "catch",
33 "char",
34 "char16_t",
35 "char32_t",
36 "class",
37 "compl",
38 "const",
39 "constexpr",
40 "const_cast",
41 "continue",
42 "decltype",
43 "default",
44 "delete",
45 "do",
46 "double",
47 "dynamic_cast",
48 "else",
49 "enum",
50 "explicit",
51 "export",
52 "extern",
53 "false",
54 "float",
55 "for",
56 "friend",
57 "goto",
58 "if",
59 "inline",
60 "int",
61 "long",
62 "mutable",
63 "namespace",
64 "new",
65 "noexcept",
66 "not",
67 "not_eq",
68 "nullptr",
69 "operator",
70 "or",
71 "or_eq",
72 "private",
73 "protected",
74 "public",
75 "register",
76 "reinterpret_cast",
77 "return",
78 "short",
79 "signed",
80 "sizeof",
81 "static",
82 "static_assert",
83 "static_cast",
84 "struct",
85 "switch",
86 "template",
87 "this",
88 "thread_local",
89 "throw",
90 "true",
91 "try",
92 "typedef",
93 "typeid",
94 "typename",
95 "union",
96 "unsigned",
97 "using",
98 "virtual",
99 "void",
100 "volatile",
101 "wchar_t",
102 "while",
103 "xor",
104 "xor_eq",
105];
106
107#[must_use]
109pub fn is_reserved(name: &str) -> bool {
110 CPP_RESERVED.contains(&name)
111}
112
113pub fn check_identifier(name: &str) -> Result<(), CppGenError> {
119 if is_reserved(name) {
120 return Err(CppGenError::InvalidName {
121 name: name.to_string(),
122 reason: "reserved C++ keyword".to_string(),
123 });
124 }
125 Ok(())
126}
127
128#[must_use]
132pub fn primitive_to_cpp(p: PrimitiveType) -> &'static str {
133 match p {
134 PrimitiveType::Boolean => "bool",
135 PrimitiveType::Octet => "uint8_t",
136 PrimitiveType::Char => "char",
137 PrimitiveType::WideChar => "wchar_t",
138 PrimitiveType::Integer(i) => integer_to_cpp(i),
139 PrimitiveType::Floating(f) => floating_to_cpp(f),
140 }
141}
142
143#[must_use]
145pub fn integer_to_cpp(i: IntegerType) -> &'static str {
146 match i {
147 IntegerType::Short | IntegerType::Int16 => "int16_t",
148 IntegerType::Long | IntegerType::Int32 => "int32_t",
149 IntegerType::LongLong | IntegerType::Int64 => "int64_t",
150 IntegerType::UShort | IntegerType::UInt16 => "uint16_t",
151 IntegerType::ULong | IntegerType::UInt32 => "uint32_t",
152 IntegerType::ULongLong | IntegerType::UInt64 => "uint64_t",
153 IntegerType::Int8 => "int8_t",
154 IntegerType::UInt8 => "uint8_t",
155 }
156}
157
158#[must_use]
161pub fn floating_to_cpp(f: FloatingType) -> &'static str {
162 match f {
163 FloatingType::Float => "float",
164 FloatingType::Double => "double",
165 FloatingType::LongDouble => "long double",
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 #![allow(clippy::expect_used, clippy::panic)]
172 use super::*;
173
174 #[test]
175 fn primitive_boolean() {
176 assert_eq!(primitive_to_cpp(PrimitiveType::Boolean), "bool");
177 }
178
179 #[test]
180 fn primitive_octet() {
181 assert_eq!(primitive_to_cpp(PrimitiveType::Octet), "uint8_t");
182 }
183
184 #[test]
185 fn primitive_char() {
186 assert_eq!(primitive_to_cpp(PrimitiveType::Char), "char");
187 }
188
189 #[test]
190 fn primitive_wchar() {
191 assert_eq!(primitive_to_cpp(PrimitiveType::WideChar), "wchar_t");
192 }
193
194 #[test]
195 fn integer_short_signed_unsigned() {
196 assert_eq!(integer_to_cpp(IntegerType::Short), "int16_t");
197 assert_eq!(integer_to_cpp(IntegerType::UShort), "uint16_t");
198 }
199
200 #[test]
201 fn integer_long_signed_unsigned() {
202 assert_eq!(integer_to_cpp(IntegerType::Long), "int32_t");
203 assert_eq!(integer_to_cpp(IntegerType::ULong), "uint32_t");
204 }
205
206 #[test]
207 fn integer_long_long_signed_unsigned() {
208 assert_eq!(integer_to_cpp(IntegerType::LongLong), "int64_t");
209 assert_eq!(integer_to_cpp(IntegerType::ULongLong), "uint64_t");
210 }
211
212 #[test]
213 fn integer_explicit_widths() {
214 assert_eq!(integer_to_cpp(IntegerType::Int8), "int8_t");
215 assert_eq!(integer_to_cpp(IntegerType::UInt8), "uint8_t");
216 assert_eq!(integer_to_cpp(IntegerType::Int16), "int16_t");
217 assert_eq!(integer_to_cpp(IntegerType::UInt16), "uint16_t");
218 assert_eq!(integer_to_cpp(IntegerType::Int32), "int32_t");
219 assert_eq!(integer_to_cpp(IntegerType::UInt32), "uint32_t");
220 assert_eq!(integer_to_cpp(IntegerType::Int64), "int64_t");
221 assert_eq!(integer_to_cpp(IntegerType::UInt64), "uint64_t");
222 }
223
224 #[test]
225 fn floating_float_double() {
226 assert_eq!(floating_to_cpp(FloatingType::Float), "float");
227 assert_eq!(floating_to_cpp(FloatingType::Double), "double");
228 }
229
230 #[test]
231 fn reserved_class_is_rejected() {
232 assert!(is_reserved("class"));
233 assert!(check_identifier("class").is_err());
234 }
235
236 #[test]
237 fn reserved_int_is_rejected() {
238 assert!(is_reserved("int"));
239 }
240
241 #[test]
242 fn non_reserved_identifier_passes() {
243 assert!(!is_reserved("Foo"));
244 assert!(check_identifier("Foo").is_ok());
245 }
246
247 #[test]
248 fn check_returns_invalidname_with_reason() {
249 let err = check_identifier("template").expect_err("must reject 'template'");
250 match err {
251 CppGenError::InvalidName { reason, name } => {
252 assert_eq!(name, "template");
253 assert!(reason.to_lowercase().contains("reserved"));
254 }
255 other => panic!("unexpected err variant: {other:?}"),
256 }
257 }
258}