Skip to main content

zerodds_idl_cpp/
error.rs

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