Skip to main content

zerodds_idl_java/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Error types for the IDL→Java codegen.
4
5use core::fmt;
6
7/// Top-level error of the Java code generator.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum JavaGenError {
10    /// The IDL construct is not supported in the current foundation
11    /// scope (C5.4-a/-b). `construct` is a short label
12    /// (e.g. `"interface"`, `"valuetype"`, `"fixed"`, `"any"`,
13    /// `"map"`); since C5.4-b it can also be a bitset/bitmask
14    /// constraint violation (e.g. `"bitset width > 64"`).
15    UnsupportedConstruct {
16        /// Name of the unsupported construct.
17        construct: String,
18        /// Optional: identifier name (type name or member name).
19        context: Option<String>,
20    },
21    /// Identifier collides with a reserved Java keyword.
22    /// Java has no `@`-escape syntax, so the identifier is
23    /// renamed with a `_` suffix by the emitter; this error only
24    /// occurs if the sanitized name itself collides or is empty.
25    InvalidName {
26        /// The invalid identifier.
27        name: String,
28        /// Reason for the rejection.
29        reason: String,
30    },
31    /// Inheritance cycle in the struct graph (self-reference or
32    /// indirect loop). Detected before emission.
33    InheritanceCycle {
34        /// Type name involved in the cycle.
35        type_name: String,
36    },
37    /// The generated output is internally inconsistent (should not
38    /// happen — bug indicator).
39    Internal(String),
40}
41
42impl fmt::Display for JavaGenError {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        match self {
45            Self::UnsupportedConstruct { construct, context } => match context {
46                Some(ctx) => write!(
47                    f,
48                    "unsupported IDL construct '{construct}' in '{ctx}' (idl-java foundation)",
49                ),
50                None => write!(
51                    f,
52                    "unsupported IDL construct '{construct}' (idl-java foundation)",
53                ),
54            },
55            Self::InvalidName { name, reason } => {
56                write!(f, "invalid identifier '{name}': {reason}")
57            }
58            Self::InheritanceCycle { type_name } => {
59                write!(f, "inheritance cycle detected at type '{type_name}'")
60            }
61            Self::Internal(msg) => write!(f, "internal codegen error: {msg}"),
62        }
63    }
64}
65
66impl std::error::Error for JavaGenError {}
67
68#[cfg(test)]
69mod tests {
70    #![allow(clippy::expect_used, clippy::panic)]
71    use super::*;
72
73    #[test]
74    fn unsupported_display_has_context() {
75        let e = JavaGenError::UnsupportedConstruct {
76            construct: "interface".into(),
77            context: Some("Foo".into()),
78        };
79        let s = format!("{e}");
80        assert!(s.contains("interface"));
81        assert!(s.contains("Foo"));
82    }
83
84    #[test]
85    fn unsupported_display_without_context() {
86        let e = JavaGenError::UnsupportedConstruct {
87            construct: "any".into(),
88            context: None,
89        };
90        let s = format!("{e}");
91        assert!(s.contains("any"));
92    }
93
94    #[test]
95    fn invalid_name_display() {
96        let e = JavaGenError::InvalidName {
97            name: "class".into(),
98            reason: "reserved Java keyword".into(),
99        };
100        assert!(format!("{e}").contains("reserved"));
101    }
102
103    #[test]
104    fn inheritance_cycle_display() {
105        let e = JavaGenError::InheritanceCycle {
106            type_name: "Loop".into(),
107        };
108        assert!(format!("{e}").contains("Loop"));
109    }
110
111    #[test]
112    fn internal_display() {
113        let e = JavaGenError::Internal("oops".into());
114        assert!(format!("{e}").contains("oops"));
115    }
116}