Skip to main content

zlink_core/idl/
custom_enum.rs

1//! Enum type definition for Varlink IDL.
2
3use core::fmt;
4
5use alloc::vec::Vec;
6
7use super::{EnumVariant, List};
8
9/// An enum type definition in Varlink IDL (enum-like with named variants).
10#[derive(Debug, Clone, Eq)]
11pub struct CustomEnum<'a> {
12    /// The name of the enum type.
13    name: &'a str,
14    /// The variants of the enum type.
15    variants: List<'a, EnumVariant<'a>>,
16    /// The comments associated with this enum type.
17    comments: List<'a, super::Comment<'a>>,
18}
19
20impl<'a> CustomEnum<'a> {
21    /// Creates a new enum type with the given name, borrowed variants, and comments.
22    pub const fn new(
23        name: &'a str,
24        variants: &'a [&'a EnumVariant<'a>],
25        comments: &'a [&'a super::Comment<'a>],
26    ) -> Self {
27        Self {
28            name,
29            variants: List::Borrowed(variants),
30            comments: List::Borrowed(comments),
31        }
32    }
33
34    /// Creates a new enum type with the given name, owned variants, and comments.
35    pub fn new_owned(
36        name: &'a str,
37        variants: Vec<EnumVariant<'a>>,
38        comments: Vec<super::Comment<'a>>,
39    ) -> Self {
40        Self {
41            name,
42            variants: List::from(variants),
43            comments: List::from(comments),
44        }
45    }
46
47    /// Returns the name of the enum type.
48    pub fn name(&self) -> &'a str {
49        self.name
50    }
51
52    /// Returns an iterator over the variants of the enum type.
53    pub fn variants(&self) -> impl Iterator<Item = &EnumVariant<'a>> {
54        self.variants.iter()
55    }
56
57    /// Returns an iterator over the comments associated with this enum type.
58    pub fn comments(&self) -> impl Iterator<Item = &super::Comment<'a>> {
59        self.comments.iter()
60    }
61}
62
63impl<'a> fmt::Display for CustomEnum<'a> {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        // Comments first
66        for comment in self.comments.iter() {
67            writeln!(f, "{comment}")?;
68        }
69
70        // Check if any variant has comments to determine formatting
71        let has_variant_comments = self.variants.iter().any(|v| v.has_comments());
72
73        if has_variant_comments {
74            // Multi-line format when any variant has comments
75            writeln!(f, "type {} (", self.name)?;
76            for variant in self.variants.iter() {
77                // Write comments first
78                for comment in variant.comments() {
79                    writeln!(f, "\t{}", comment)?;
80                }
81                // Then write the variant name
82                writeln!(f, "\t{}", variant.name())?;
83            }
84            write!(f, ")")
85        } else {
86            // Single-line format when no variants have comments
87            write!(f, "type {} (", self.name)?;
88            let mut first = true;
89            for variant in self.variants.iter() {
90                if !first {
91                    write!(f, ", ")?;
92                }
93                first = false;
94                write!(f, "{}", variant)?;
95            }
96            write!(f, ")")
97        }
98    }
99}
100
101impl<'a> PartialEq for CustomEnum<'a> {
102    fn eq(&self, other: &Self) -> bool {
103        self.name == other.name && self.variants == other.variants
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use alloc::vec;
110
111    use super::*;
112    use crate::idl::{Comment, EnumVariant};
113    use core::fmt::Write;
114
115    #[test]
116    fn display_with_comments() {
117        let comment1 = Comment::new("Status enumeration");
118        let comment2 = Comment::new("Represents current state");
119        let comments = [&comment1, &comment2];
120
121        let var1 = EnumVariant::new("active", &[]);
122        let var2 = EnumVariant::new("inactive", &[]);
123        let var3 = EnumVariant::new("pending", &[]);
124        let variants = [&var1, &var2, &var3];
125
126        let custom_enum = CustomEnum::new("Status", &variants, &comments);
127        let mut displayed = String::new();
128        write!(&mut displayed, "{}", custom_enum).unwrap();
129        assert_eq!(
130            displayed,
131            "# Status enumeration\n# Represents current state\ntype Status (active, inactive, pending)"
132        );
133    }
134
135    #[test]
136    fn display_with_variant_comments() {
137        let var_comment = Comment::new("The active state");
138        let var1 = EnumVariant::new_owned("active", vec![var_comment]);
139        let var2 = EnumVariant::new_owned("inactive", vec![]);
140        let custom_enum = CustomEnum::new_owned("Status", vec![var1, var2], vec![]);
141
142        let mut displayed = String::new();
143        write!(&mut displayed, "{}", custom_enum).unwrap();
144        assert_eq!(
145            displayed,
146            "type Status (\n\t# The active state\n\tactive\n\tinactive\n)"
147        );
148    }
149
150    #[test_log::test]
151    fn comprehensive_enum_with_per_variant_comments() {
152        // Test enum-level comments plus per-variant comments
153        let enum_comment = Comment::new("Status enumeration with detailed docs");
154
155        let active_comment = Comment::new("System is operational");
156        let inactive_comment = Comment::new("System is stopped");
157        let pending_comment = Comment::new("System is starting up");
158
159        let var1 = EnumVariant::new_owned("active", vec![active_comment]);
160        let var2 = EnumVariant::new_owned("inactive", vec![inactive_comment]);
161        let var3 = EnumVariant::new_owned("pending", vec![pending_comment]);
162
163        let custom_enum =
164            CustomEnum::new_owned("SystemStatus", vec![var1, var2, var3], vec![enum_comment]);
165
166        // Test that we can access all the information
167        assert_eq!(custom_enum.name(), "SystemStatus");
168        assert_eq!(custom_enum.variants().count(), 3);
169        assert_eq!(custom_enum.comments().count(), 1);
170
171        // Test display output includes both enum and variant comments
172        let mut displayed = String::new();
173        write!(&mut displayed, "{}", custom_enum).unwrap();
174
175        // Should contain enum comment
176        assert!(displayed.contains("Status enumeration with detailed docs"));
177        // Should contain variant comments on separate lines
178        assert!(displayed.contains("# System is operational\n\tactive"));
179        assert!(displayed.contains("# System is stopped\n\tinactive"));
180        assert!(displayed.contains("# System is starting up\n\tpending"));
181
182        debug!("✓ Comprehensive enum display: {}", displayed);
183    }
184
185    #[test]
186    fn formatting_with_and_without_comments() {
187        // Test single-line format when no variants have comments
188        let var1 = EnumVariant::new("red", &[]);
189        let var2 = EnumVariant::new("green", &[]);
190        let var3 = EnumVariant::new("blue", &[]);
191        let variants_no_comments = [&var1, &var2, &var3];
192
193        let enum_no_comments = CustomEnum::new("Color", &variants_no_comments, &[]);
194        let mut displayed = String::new();
195        write!(&mut displayed, "{}", enum_no_comments).unwrap();
196        assert_eq!(displayed, "type Color (red, green, blue)");
197
198        // Test multi-line format when any variant has comments
199        let comment = Comment::new("Primary color");
200        let comment_refs = [&comment];
201        let var_with_comment = EnumVariant::new("red", &comment_refs);
202        let var_without_comment1 = EnumVariant::new("green", &[]);
203        let var_without_comment2 = EnumVariant::new("blue", &[]);
204        let variants_with_comments = [
205            &var_with_comment,
206            &var_without_comment1,
207            &var_without_comment2,
208        ];
209
210        let enum_with_comments = CustomEnum::new("Color", &variants_with_comments, &[]);
211        let mut displayed = String::new();
212        write!(&mut displayed, "{}", enum_with_comments).unwrap();
213        assert_eq!(
214            displayed,
215            "type Color (\n\t# Primary color\n\tred\n\tgreen\n\tblue\n)"
216        );
217    }
218}