1mod type_ref;
4pub use type_ref::TypeRef;
5
6use core::fmt;
7
8use super::{EnumVariant, Field, List};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum Type<'a> {
13 Bool,
15 Int,
17 Float,
19 String,
21 ForeignObject,
23 Any,
25 Optional(TypeRef<'a>),
27 Array(TypeRef<'a>),
29 Map(TypeRef<'a>),
31 Custom(&'a str),
33 Enum(List<'a, EnumVariant<'a>>),
35 Object(List<'a, Field<'a>>),
37}
38
39impl<'a> Type<'a> {
40 pub const fn as_optional(&self) -> Option<&TypeRef<'a>> {
42 match self {
43 Type::Optional(optional) => Some(optional),
44 _ => None,
45 }
46 }
47
48 pub const fn as_array(&self) -> Option<&TypeRef<'a>> {
50 match self {
51 Type::Array(array) => Some(array),
52 _ => None,
53 }
54 }
55
56 pub const fn as_map(&self) -> Option<&TypeRef<'a>> {
58 match self {
59 Type::Map(map) => Some(map),
60 _ => None,
61 }
62 }
63
64 pub const fn as_custom(&self) -> Option<&'a str> {
66 match self {
67 Type::Custom(custom) => Some(custom),
68 _ => None,
69 }
70 }
71
72 pub const fn as_enum(&self) -> Option<&List<'a, EnumVariant<'a>>> {
74 match self {
75 Type::Enum(variants) => Some(variants),
76 _ => None,
77 }
78 }
79
80 pub const fn as_object(&self) -> Option<&List<'a, Field<'a>>> {
82 match self {
83 Type::Object(fields) => Some(fields),
84 _ => None,
85 }
86 }
87}
88
89impl<'a> fmt::Display for Type<'a> {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self {
92 Type::Bool => write!(f, "bool"),
93 Type::Int => write!(f, "int"),
94 Type::Float => write!(f, "float"),
95 Type::String => write!(f, "string"),
96 Type::ForeignObject => write!(f, "object"),
97 Type::Any => write!(f, "any"),
98 Type::Optional(optional) => write!(f, "?{optional}"),
99 Type::Array(array) => write!(f, "[]{array}"),
100 Type::Map(map) => write!(f, "[string]{map}"),
101 Type::Custom(name) => write!(f, "{name}"),
102 Type::Enum(variants) => {
103 let has_variant_comments = variants.iter().any(|v| v.has_comments());
105
106 if has_variant_comments {
107 writeln!(f, "(")?;
109 for variant in variants.iter() {
110 for comment in variant.comments() {
112 writeln!(f, "\t{}", comment)?;
113 }
114 writeln!(f, "\t{}", variant.name())?;
116 }
117 write!(f, ")")
118 } else {
119 write!(f, "(")?;
121 let mut first = true;
122 for variant in variants.iter() {
123 if !first {
124 write!(f, ", ")?;
125 }
126 first = false;
127 write!(f, "{}", variant)?;
128 }
129 write!(f, ")")
130 }
131 }
132 Type::Object(fields) => {
133 write!(f, "(")?;
134 let mut first = true;
135 for field in fields.iter() {
136 if !first {
137 write!(f, ", ")?;
138 }
139 first = false;
140 write!(f, "{field}")?;
141 }
142 write!(f, ")")
143 }
144 }
145 }
146}
147
148impl<'a> PartialEq<TypeRef<'a>> for Type<'a> {
149 fn eq(&self, other: &TypeRef<'a>) -> bool {
150 self == other.inner()
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use alloc::{string::ToString, vec};
157
158 use super::*;
159 use crate::{Comment, EnumVariant};
160
161 #[test]
162 fn type_names() {
163 use core::fmt::Write;
164 let mut buf = String::new();
165
166 buf.clear();
167 write!(buf, "{}", Type::Bool).unwrap();
168 assert_eq!(buf, "bool");
169
170 buf.clear();
171 write!(buf, "{}", Type::Int).unwrap();
172 assert_eq!(buf, "int");
173
174 buf.clear();
175 write!(buf, "{}", Type::Float).unwrap();
176 assert_eq!(buf, "float");
177
178 buf.clear();
179 write!(buf, "{}", Type::String).unwrap();
180 assert_eq!(buf, "string");
181
182 buf.clear();
183 write!(buf, "{}", Type::ForeignObject).unwrap();
184 assert_eq!(buf, "object");
185
186 buf.clear();
187 write!(buf, "{}", Type::Any).unwrap();
188 assert_eq!(buf, "any");
189 }
190
191 #[test]
192 fn complex_type_names() {
193 const INT_TYPE: Type<'static> = Type::Int;
195 const STRING_TYPE: Type<'static> = Type::String;
196 const BOOL_TYPE: Type<'static> = Type::Bool;
197
198 use core::fmt::Write;
199 let mut buf = String::new();
200
201 buf.clear();
202 write!(buf, "{}", Type::Optional(TypeRef::new(&INT_TYPE))).unwrap();
203 assert_eq!(buf, "?int");
204
205 buf.clear();
206 write!(buf, "{}", Type::Array(TypeRef::new(&STRING_TYPE))).unwrap();
207 assert_eq!(buf, "[]string");
208
209 buf.clear();
210 write!(buf, "{}", Type::Map(TypeRef::new(&BOOL_TYPE))).unwrap();
211 assert_eq!(buf, "[string]bool");
212
213 assert_eq!(
215 Type::Optional(TypeRef::new_owned(Type::Int)).to_string(),
216 "?int"
217 );
218 assert_eq!(
219 Type::Array(TypeRef::new_owned(Type::String)).to_string(),
220 "[]string"
221 );
222
223 let nested_type = Type::Array(TypeRef::new_owned(Type::Optional(TypeRef::new_owned(
225 Type::String,
226 ))));
227 assert_eq!(nested_type.to_string(), "[]?string");
228
229 let enum_type = Type::Enum(List::from(vec![
231 EnumVariant::new("one", &[]),
232 EnumVariant::new("two", &[]),
233 EnumVariant::new("three", &[]),
234 ]));
235 assert_eq!(enum_type.to_string(), "(one, two, three)");
236
237 let struct_type = Type::Object(List::from(vec![
239 Field::new("first", &Type::Int, &[]),
240 Field::new("second", &Type::String, &[]),
241 ]));
242 assert_eq!(struct_type.to_string(), "(first: int, second: string)");
243 }
244
245 #[test]
246 fn inline_enum_formatting_with_and_without_comments() {
247 use core::fmt::Write;
248
249 let var1 = EnumVariant::new("red", &[]);
251 let var2 = EnumVariant::new("green", &[]);
252 let var3 = EnumVariant::new("blue", &[]);
253 let variants_no_comments = [&var1, &var2, &var3];
254 let enum_no_comments = Type::Enum(List::from(&variants_no_comments[..]));
255
256 let mut buf = String::new();
257 write!(buf, "{}", enum_no_comments).unwrap();
258 assert_eq!(buf, "(red, green, blue)");
259
260 let comment_refs = [&Comment::new("Primary color")];
262 let var_with_comment = EnumVariant::new("red", &comment_refs);
263 let var_without_comment1 = EnumVariant::new("green", &[]);
264 let var_without_comment2 = EnumVariant::new("blue", &[]);
265 let variants_with_comments = [
266 &var_with_comment,
267 &var_without_comment1,
268 &var_without_comment2,
269 ];
270 let enum_with_comments = Type::Enum(List::from(&variants_with_comments[..]));
271
272 let mut buf = String::new();
273 write!(buf, "{}", enum_with_comments).unwrap();
274 assert_eq!(buf, "(\n\t# Primary color\n\tred\n\tgreen\n\tblue\n)");
275 }
276}