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            let last = self.variants.len().saturating_sub(1);
77            for (i, variant) in self.variants.iter().enumerate() {
78                // Write comments first
79                for comment in variant.comments() {
80                    writeln!(f, "\t{}", comment)?;
81                }
82                // Then write the variant name
83                if i == last {
84                    writeln!(f, "\t{}", variant.name())?;
85                } else {
86                    writeln!(f, "\t{},", variant.name())?;
87                }
88            }
89            write!(f, ")")
90        } else {
91            // Single-line format when no variants have comments
92            write!(f, "type {} (", self.name)?;
93            let mut first = true;
94            for variant in self.variants.iter() {
95                if !first {
96                    write!(f, ", ")?;
97                }
98                first = false;
99                write!(f, "{}", variant)?;
100            }
101            write!(f, ")")
102        }
103    }
104}
105
106impl<'a> PartialEq for CustomEnum<'a> {
107    fn eq(&self, other: &Self) -> bool {
108        self.name == other.name && self.variants == other.variants
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use alloc::vec;
115
116    use super::*;
117    use crate::idl::{Comment, EnumVariant};
118    use core::fmt::Write;
119
120    #[test]
121    fn display_with_comments() {
122        let comment1 = Comment::new("Status enumeration");
123        let comment2 = Comment::new("Represents current state");
124        let comments = [&comment1, &comment2];
125
126        let var1 = EnumVariant::new("active", &[]);
127        let var2 = EnumVariant::new("inactive", &[]);
128        let var3 = EnumVariant::new("pending", &[]);
129        let variants = [&var1, &var2, &var3];
130
131        let custom_enum = CustomEnum::new("Status", &variants, &comments);
132        let mut displayed = String::new();
133        write!(&mut displayed, "{}", custom_enum).unwrap();
134        assert_eq!(
135            displayed,
136            "# Status enumeration\n# Represents current state\ntype Status (active, inactive, pending)"
137        );
138    }
139
140    #[test]
141    fn display_with_variant_comments() {
142        let var_comment = Comment::new("The active state");
143        let var1 = EnumVariant::new_owned("active", vec![var_comment]);
144        let var2 = EnumVariant::new_owned("inactive", vec![]);
145        let custom_enum = CustomEnum::new_owned("Status", vec![var1, var2], vec![]);
146
147        let mut displayed = String::new();
148        write!(&mut displayed, "{}", custom_enum).unwrap();
149        assert_eq!(
150            displayed,
151            "type Status (\n\t# The active state\n\tactive,\n\tinactive\n)"
152        );
153    }
154
155    #[test_log::test]
156    fn comprehensive_enum_with_per_variant_comments() {
157        // Test enum-level comments plus per-variant comments
158        let enum_comment = Comment::new("Status enumeration with detailed docs");
159
160        let active_comment = Comment::new("System is operational");
161        let inactive_comment = Comment::new("System is stopped");
162        let pending_comment = Comment::new("System is starting up");
163
164        let var1 = EnumVariant::new_owned("active", vec![active_comment]);
165        let var2 = EnumVariant::new_owned("inactive", vec![inactive_comment]);
166        let var3 = EnumVariant::new_owned("pending", vec![pending_comment]);
167
168        let custom_enum =
169            CustomEnum::new_owned("SystemStatus", vec![var1, var2, var3], vec![enum_comment]);
170
171        // Test that we can access all the information
172        assert_eq!(custom_enum.name(), "SystemStatus");
173        assert_eq!(custom_enum.variants().count(), 3);
174        assert_eq!(custom_enum.comments().count(), 1);
175
176        // Test display output includes both enum and variant comments
177        let mut displayed = String::new();
178        write!(&mut displayed, "{}", custom_enum).unwrap();
179
180        // Should contain enum comment
181        assert!(displayed.contains("Status enumeration with detailed docs"));
182        // Should contain variant comments on separate lines
183        assert!(displayed.contains("# System is operational\n\tactive"));
184        assert!(displayed.contains("# System is stopped\n\tinactive"));
185        assert!(displayed.contains("# System is starting up\n\tpending"));
186
187        debug!("✓ Comprehensive enum display: {}", displayed);
188    }
189
190    #[test]
191    fn formatting_with_and_without_comments() {
192        // Test single-line format when no variants have comments
193        let var1 = EnumVariant::new("red", &[]);
194        let var2 = EnumVariant::new("green", &[]);
195        let var3 = EnumVariant::new("blue", &[]);
196        let variants_no_comments = [&var1, &var2, &var3];
197
198        let enum_no_comments = CustomEnum::new("Color", &variants_no_comments, &[]);
199        let mut displayed = String::new();
200        write!(&mut displayed, "{}", enum_no_comments).unwrap();
201        assert_eq!(displayed, "type Color (red, green, blue)");
202
203        // Test multi-line format when any variant has comments
204        let comment = Comment::new("Primary color");
205        let comment_refs = [&comment];
206        let var_with_comment = EnumVariant::new("red", &comment_refs);
207        let var_without_comment1 = EnumVariant::new("green", &[]);
208        let var_without_comment2 = EnumVariant::new("blue", &[]);
209        let variants_with_comments = [
210            &var_with_comment,
211            &var_without_comment1,
212            &var_without_comment2,
213        ];
214
215        let enum_with_comments = CustomEnum::new("Color", &variants_with_comments, &[]);
216        let mut displayed = String::new();
217        write!(&mut displayed, "{}", enum_with_comments).unwrap();
218        assert_eq!(
219            displayed,
220            "type Color (\n\t# Primary color\n\tred,\n\tgreen,\n\tblue\n)"
221        );
222    }
223}