Skip to main content

zerodds_idl_java/
keywords.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Java-17-Keywords (inkl. Restricted Identifiers wie `record`, `sealed`,
4//! `var`, `yield`).
5//!
6//! Quelle: JLS 17 §3.9 + §3.8 (restricted identifiers).
7//!
8//! Wenn ein IDL-Identifier einen Java-Keyword trifft, kann der Generator
9//! ihn nicht ueber `@`-Escape ausweichen (Java hat keine solche Syntax).
10//! Stattdessen wird der Name mit Unterstrich-Suffix versehen
11//! (`class` → `class_`).
12
13use crate::error::JavaGenError;
14
15/// Reservierte Java-17-Schluesselwoerter (inkl. Restricted Identifiers).
16///
17/// Quelle: JLS 17 §3.9 ("Keywords"), §3.8 ("Identifiers"; restricted)
18/// und §3.10.3 (Boolean / null Literals).
19pub(crate) const JAVA_RESERVED: &[&str] = &[
20    // 50 echte Keywords (JLS §3.9)
21    "abstract",
22    "assert",
23    "boolean",
24    "break",
25    "byte",
26    "case",
27    "catch",
28    "char",
29    "class",
30    "const",
31    "continue",
32    "default",
33    "do",
34    "double",
35    "else",
36    "enum",
37    "extends",
38    "final",
39    "finally",
40    "float",
41    "for",
42    "goto",
43    "if",
44    "implements",
45    "import",
46    "instanceof",
47    "int",
48    "interface",
49    "long",
50    "native",
51    "new",
52    "package",
53    "private",
54    "protected",
55    "public",
56    "return",
57    "short",
58    "static",
59    "strictfp",
60    "super",
61    "switch",
62    "synchronized",
63    "this",
64    "throw",
65    "throws",
66    "transient",
67    "try",
68    "void",
69    "volatile",
70    "while",
71    // Boolean / null-Literale (§3.10.3) — koennen nicht als Identifier
72    // verwendet werden.
73    "true",
74    "false",
75    "null",
76    // Restricted Identifiers (§3.8) — innerhalb gewisser Kontexte
77    // reserviert; wir behandeln sie konservativ als reserviert.
78    "record",
79    "sealed",
80    "var",
81    "yield",
82    "non-sealed",
83    "permits",
84    "exports",
85    "module",
86    "open",
87    "opens",
88    "provides",
89    "requires",
90    "to",
91    "transitive",
92    "uses",
93    "with",
94];
95
96/// Prueft, ob ein Identifier ein Java-Keyword ist.
97#[must_use]
98pub fn is_reserved(name: &str) -> bool {
99    JAVA_RESERVED.contains(&name)
100}
101
102/// Bereinigt einen IDL-Identifier fuer Java.
103///
104/// Wenn der Name reserviert ist, wird ein `_`-Suffix angehaengt
105/// (`class` → `class_`). Andernfalls bleibt er unveraendert.
106///
107/// # Errors
108/// Liefert [`JavaGenError::InvalidName`], wenn der Name leer ist.
109pub fn sanitize_identifier(name: &str) -> Result<String, JavaGenError> {
110    if name.is_empty() {
111        return Err(JavaGenError::InvalidName {
112            name: name.to_string(),
113            reason: "empty identifier".to_string(),
114        });
115    }
116    if is_reserved(name) {
117        Ok(format!("{name}_"))
118    } else {
119        Ok(name.to_string())
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    #![allow(clippy::expect_used, clippy::panic)]
126    use super::*;
127
128    #[test]
129    fn class_is_reserved() {
130        assert!(is_reserved("class"));
131    }
132
133    #[test]
134    fn record_is_reserved() {
135        assert!(is_reserved("record"));
136    }
137
138    #[test]
139    fn sealed_is_reserved() {
140        assert!(is_reserved("sealed"));
141    }
142
143    #[test]
144    fn var_yield_reserved() {
145        assert!(is_reserved("var"));
146        assert!(is_reserved("yield"));
147    }
148
149    #[test]
150    fn foo_is_not_reserved() {
151        assert!(!is_reserved("Foo"));
152    }
153
154    #[test]
155    fn sanitize_keyword_appends_underscore() {
156        assert_eq!(sanitize_identifier("class").expect("ok"), "class_");
157        assert_eq!(sanitize_identifier("int").expect("ok"), "int_");
158    }
159
160    #[test]
161    fn sanitize_normal_passthrough() {
162        assert_eq!(sanitize_identifier("Foo").expect("ok"), "Foo");
163    }
164
165    #[test]
166    fn sanitize_empty_errors() {
167        assert!(matches!(
168            sanitize_identifier(""),
169            Err(JavaGenError::InvalidName { .. })
170        ));
171    }
172
173    #[test]
174    fn list_contains_at_least_50_keywords() {
175        // JLS-§3.9 hat 50 echte Keywords; insgesamt mit Restricted-IDs
176        // erwarten wir > 60.
177        assert!(JAVA_RESERVED.len() >= 50);
178    }
179}