Skip to main content

zlink_idl/
custom_type.rs

1//! Type definition for Varlink IDL custom types.
2
3use core::fmt;
4
5use super::{CustomEnum, CustomObject};
6
7/// A custom type definition in Varlink IDL.
8///
9/// This can be either a struct-like object type with named fields,
10/// or an enum-like type with named variants.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum CustomType<'a> {
13    /// A struct-like custom type with named fields.
14    Object(CustomObject<'a>),
15    /// An enum-like custom type with named variants.
16    Enum(CustomEnum<'a>),
17}
18
19impl<'a> CustomType<'a> {
20    /// Returns the name of the custom type.
21    pub fn name(&self) -> &'a str {
22        match self {
23            CustomType::Object(obj) => obj.name(),
24            CustomType::Enum(enm) => enm.name(),
25        }
26    }
27
28    /// Returns true if this is an object custom type.
29    pub fn is_object(&self) -> bool {
30        matches!(self, CustomType::Object(_))
31    }
32
33    /// Returns true if this is an enum custom type.
34    pub fn is_enum(&self) -> bool {
35        matches!(self, CustomType::Enum(_))
36    }
37
38    /// Returns the object if this is an object custom type.
39    pub const fn as_object(&self) -> Option<&CustomObject<'a>> {
40        match self {
41            CustomType::Object(obj) => Some(obj),
42            CustomType::Enum(_) => None,
43        }
44    }
45
46    /// Returns the enum if this is an enum custom type.
47    pub fn as_enum(&self) -> Option<&CustomEnum<'a>> {
48        match self {
49            CustomType::Object(_) => None,
50            CustomType::Enum(enm) => Some(enm),
51        }
52    }
53}
54
55impl<'a> From<CustomObject<'a>> for CustomType<'a> {
56    fn from(obj: CustomObject<'a>) -> Self {
57        CustomType::Object(obj)
58    }
59}
60
61impl<'a> From<CustomEnum<'a>> for CustomType<'a> {
62    fn from(enm: CustomEnum<'a>) -> Self {
63        CustomType::Enum(enm)
64    }
65}
66
67impl<'a> fmt::Display for CustomType<'a> {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            CustomType::Object(obj) => write!(f, "{obj}"),
71            CustomType::Enum(enm) => write!(f, "{enm}"),
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use alloc::vec;
79
80    use super::*;
81    use crate::{EnumVariant, Field, Type};
82
83    #[test]
84    fn object_creation() {
85        let field_x = Field::new("x", &Type::Float, &[]);
86        let field_y = Field::new("y", &Type::Float, &[]);
87        let fields = [&field_x, &field_y];
88
89        let custom_obj = CustomObject::new("Point", &fields, &[]);
90        assert_eq!(custom_obj.name(), "Point");
91        assert_eq!(custom_obj.fields().count(), 2);
92
93        // Check the fields individually - order and values.
94        let fields = custom_obj.fields().collect::<Vec<_>>();
95        assert_eq!(fields[0].name(), "x");
96        assert_eq!(fields[0].ty(), &Type::Float);
97        assert_eq!(fields[1].name(), "y");
98        assert_eq!(fields[1].ty(), &Type::Float);
99    }
100
101    #[test]
102    fn enum_creation() {
103        let red = EnumVariant::new_owned("red", vec![]);
104        let green = EnumVariant::new_owned("green", vec![]);
105        let blue = EnumVariant::new_owned("blue", vec![]);
106        let custom_enum = CustomEnum::new_owned("Color", vec![red, green, blue], vec![]);
107
108        assert_eq!(custom_enum.name(), "Color");
109        assert_eq!(custom_enum.variants().count(), 3);
110
111        // Check the variants individually - order and values.
112        let variants = custom_enum.variants().collect::<Vec<_>>();
113        assert_eq!(variants[0].name(), "red");
114        assert_eq!(variants[1].name(), "green");
115        assert_eq!(variants[2].name(), "blue");
116    }
117
118    #[test]
119    fn type_from_object() {
120        let field_x = Field::new("x", &Type::Float, &[]);
121        let field_y = Field::new("y", &Type::Float, &[]);
122        let fields = [&field_x, &field_y];
123
124        let custom_obj = CustomObject::new("Point", &fields, &[]);
125        let custom_type = CustomType::from(custom_obj);
126
127        assert_eq!(custom_type.name(), "Point");
128        assert!(custom_type.is_object());
129        assert!(!custom_type.is_enum());
130        assert!(custom_type.as_object().is_some());
131        assert!(custom_type.as_enum().is_none());
132    }
133
134    #[test]
135    fn type_from_enum() {
136        let red = EnumVariant::new_owned("red", vec![]);
137        let green = EnumVariant::new_owned("green", vec![]);
138        let blue = EnumVariant::new_owned("blue", vec![]);
139        let custom_enum = CustomEnum::new_owned("Color", vec![red, green, blue], vec![]);
140        let custom_type = CustomType::from(custom_enum);
141
142        assert_eq!(custom_type.name(), "Color");
143        assert!(!custom_type.is_object());
144        assert!(custom_type.is_enum());
145        assert!(custom_type.as_object().is_none());
146        assert!(custom_type.as_enum().is_some());
147    }
148
149    #[test]
150    fn type_display_object() {
151        // Test object display
152        let field_x = Field::new("x", &Type::Int, &[]);
153        let field_y = Field::new("y", &Type::Int, &[]);
154        let fields = [&field_x, &field_y];
155        let custom_obj = CustomObject::new("Point", &fields, &[]);
156        let custom_type = CustomType::from(custom_obj);
157        use core::fmt::Write;
158        let mut buf = String::new();
159        write!(buf, "{}", custom_type).unwrap();
160        assert_eq!(buf.as_str(), "type Point (x: int, y: int)");
161    }
162
163    #[test]
164    fn type_display_enum() {
165        // Test enum display
166        const DIRECTION_VARIANTS: &[&EnumVariant<'static>] = &[
167            &EnumVariant::new("north", &[]),
168            &EnumVariant::new("south", &[]),
169            &EnumVariant::new("east", &[]),
170            &EnumVariant::new("west", &[]),
171        ];
172        let custom_enum = CustomEnum::new("Direction", DIRECTION_VARIANTS, &[]);
173        let custom_type = CustomType::from(custom_enum);
174        use core::fmt::Write;
175        let mut buf = String::new();
176        write!(buf, "{}", custom_type).unwrap();
177        assert_eq!(buf.as_str(), "type Direction (north, south, east, west)");
178    }
179
180    #[test]
181    fn owned_types() {
182        // Test owned object
183        let fields = vec![
184            Field::new("name", &Type::String, &[]),
185            Field::new("age", &Type::Int, &[]),
186        ];
187        let custom_obj = CustomObject::new_owned("Person", fields, vec![]);
188        assert_eq!(custom_obj.name(), "Person");
189        use core::fmt::Write;
190        let mut buf = String::new();
191        write!(buf, "{}", custom_obj).unwrap();
192        assert_eq!(buf.as_str(), "type Person (name: string, age: int)");
193
194        // Test owned enum
195        let custom_enum = CustomEnum::new_owned(
196            "Size",
197            vec![
198                EnumVariant::new_owned("small", vec![]),
199                EnumVariant::new_owned("medium", vec![]),
200                EnumVariant::new_owned("large", vec![]),
201            ],
202            vec![],
203        );
204        assert_eq!(custom_enum.name(), "Size");
205        let mut buf = String::new();
206        write!(buf, "{}", custom_enum).unwrap();
207        assert_eq!(buf.as_str(), "type Size (small, medium, large)");
208    }
209}