Skip to main content

zerodds_idl_csharp/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Fehler-Typen fuer den IDL → C#-Codegen.
4
5use core::fmt;
6
7/// Top-Level-Fehler des C#-Code-Generators.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum CsGenError {
10    /// IDL-Konstrukt ist im aktuellen Foundation-Scope (C5.3-a) nicht
11    /// unterstuetzt. `construct` ist eine kurze Bezeichnung (z.B.
12    /// `"interface"`, `"valuetype"`, `"fixed"`, `"any"`, `"map"`,
13    /// `"bitset"`, `"bitmask"`).
14    UnsupportedConstruct {
15        /// Name des nicht-unterstuetzten Konstrukts.
16        construct: String,
17        /// Optional: Identifier-Name (Type-Name oder Member-Name).
18        context: Option<String>,
19    },
20    /// Identifier kollidiert mit einem reservierten C#-Keyword.
21    /// Anders als bei C++ wird in C# nicht abgelehnt, sondern mit
22    /// `@`-Prefix escaped (Spec-Verhalten, §6 IDL4-CS-Mapping).
23    /// Dieser Fehler tritt nur auf, wenn der Name selbst nach Escape
24    /// noch ungueltig waere (leerer String, doppelter `@`, ...).
25    InvalidName {
26        /// Der unzulaessige Identifier.
27        name: String,
28        /// Grund der Ablehnung.
29        reason: String,
30    },
31    /// Inheritance-Cycle im Struct-Graphen (Self-Reference oder
32    /// indirekte Schleife). Wird vor der Emission erkannt.
33    InheritanceCycle {
34        /// Beteiligter Type-Name am Cycle.
35        type_name: String,
36    },
37    /// Generierter Output ist intern inkonsistent (sollte nicht
38    /// auftreten — Bug-Indikator).
39    Internal(String),
40}
41
42impl fmt::Display for CsGenError {
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}' (C5.3-a Foundation scope)",
49                ),
50                None => write!(
51                    f,
52                    "unsupported IDL construct '{construct}' (C5.3-a Foundation scope)",
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 CsGenError {}
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 = CsGenError::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 = CsGenError::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 = CsGenError::InvalidName {
97            name: "@@".into(),
98            reason: "double-escape not allowed".into(),
99        };
100        assert!(format!("{e}").contains("double-escape"));
101    }
102
103    #[test]
104    fn inheritance_cycle_display() {
105        let e = CsGenError::InheritanceCycle {
106            type_name: "Loop".into(),
107        };
108        assert!(format!("{e}").contains("Loop"));
109    }
110
111    #[test]
112    fn internal_display() {
113        let e = CsGenError::Internal("oops".into());
114        assert!(format!("{e}").contains("oops"));
115    }
116
117    #[test]
118    fn errors_are_clonable_and_eq() {
119        let e1 = CsGenError::Internal("x".into());
120        let e2 = e1.clone();
121        assert_eq!(e1, e2);
122    }
123}