1use std::fmt::{Debug, Display};
2
3use crate::value::Value;
4
5#[derive(Clone, PartialEq)]
6pub enum Type {
7 Value,
8 String,
9 Fn {
10 args: Vec<Type>,
11 variadic_arg: Option<Box<Type>>,
12 returns: Box<Type>,
13 },
14 Bool,
15}
16
17impl Type {
18 pub fn name(&self) -> String {
19 match self {
20 Type::Value => "Value".to_string(),
21 Type::String => "String".to_string(),
22 Type::Fn {
23 args,
24 variadic_arg,
25 returns,
26 } => {
27 let mut args: Vec<String> = args.iter().map(|arg| arg.name()).collect();
28
29 if let Some(varg) = variadic_arg {
30 args.push(format!("...{}", varg.name()));
31 }
32
33 let args = args.join(", ");
34
35 let returns = returns.name();
36
37 format!("Fn({args}) -> {returns}")
38 }
39 Type::Bool => "Bool".to_string(),
40 }
41 }
42}
43
44impl Display for Type {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{}", self.name())
47 }
48}
49
50impl Debug for Type {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 write!(f, "{}", self.name())
53 }
54}
55
56impl From<Value> for Type {
57 fn from(value: Value) -> Self {
58 match value {
59 Value::String(_) => Type::String,
60 Value::Fn(builtin_fn) => {
61 let mut args: Vec<Type> =
62 builtin_fn.args.iter().map(|arg| arg.ty.clone()).collect();
63
64 let variadic_arg = builtin_fn
65 .args
66 .last()
67 .filter(|arg| arg.variadic)
68 .map(|arg| Box::new(arg.ty.clone()));
69
70 if variadic_arg.is_some() {
71 args.pop();
72 }
73
74 Type::Fn {
75 args,
76 variadic_arg,
77 returns: Box::new(builtin_fn.return_type.clone()),
78 }
79 }
80 Value::Bool(_) => Type::Bool,
81 }
82 }
83}
84
85#[cfg(test)]
86mod from_tests {
87 use std::rc::Rc;
88
89 use crate::prelude::{BuiltinFn, FnArg};
90
91 use super::*;
92
93 #[test]
94 fn test_from_string_value() {
95 let string_value = Value::String("test".to_string());
96 let ty: Type = string_value.into();
97 assert_eq!(Type::String, ty);
98 }
99
100 #[test]
101 fn test_get_type_string_value() {
102 let string_value = Value::String("test".to_string());
103 let ty: Type = string_value.get_type();
104 assert_eq!(Type::String, ty);
105 }
106
107 #[test]
108 fn test_from_bool_value() {
109 let bool_value = Value::Bool(true);
110 let ty: Type = bool_value.into();
111 assert_eq!(Type::Bool, ty);
112 }
113
114 #[test]
115 fn test_get_type_bool_value() {
116 let bool_value = Value::Bool(true);
117 let ty: Type = bool_value.get_type();
118 assert_eq!(Type::Bool, ty);
119 }
120
121 #[test]
122 fn test_from_fn_value() {
123 let builtin_fn = Value::Fn(Rc::new(BuiltinFn {
124 name: "test".to_string(),
125 args: vec![FnArg::new("a", Type::Value)],
126 return_type: Type::String,
127 func: Rc::new(|_| Value::String("".to_string())),
128 }));
129
130 let ty: Type = builtin_fn.into();
131
132 assert_eq!(
133 Type::Fn {
134 args: vec![Type::Value],
135 variadic_arg: None,
136 returns: Box::new(Type::String),
137 },
138 ty
139 );
140 }
141
142 #[test]
143 fn test_get_type_fn_value() {
144 let builtin_fn = Value::Fn(Rc::new(BuiltinFn {
145 name: "test".to_string(),
146 args: vec![FnArg::new("a", Type::Value)],
147 return_type: Type::String,
148 func: Rc::new(|_| Value::String("".to_string())),
149 }));
150
151 let ty: Type = builtin_fn.get_type();
152
153 assert_eq!(
154 Type::Fn {
155 args: vec![Type::Value],
156 variadic_arg: None,
157 returns: Box::new(Type::String),
158 },
159 ty
160 );
161 }
162}
163
164#[cfg(test)]
165mod name_and_display_tests {
166 use super::*;
167
168 #[test]
169 fn test_name_value() {
170 assert_eq!("Value", Type::Value.name());
171 }
172
173 #[test]
174 fn test_name_string() {
175 assert_eq!("String", Type::String.name());
176 }
177
178 #[test]
179 fn test_name_fn_0_args() {
180 assert_eq!(
181 "Fn() -> String",
182 Type::Fn {
183 args: vec![],
184 variadic_arg: None,
185 returns: Type::String.into(),
186 }
187 .name()
188 );
189 }
190
191 #[test]
192 fn test_name_fn_0_args_and_varadic() {
193 assert_eq!(
194 "Fn(...Value) -> String",
195 Type::Fn {
196 args: vec![],
197 variadic_arg: Some(Type::Value.into()),
198 returns: Type::String.into(),
199 }
200 .name()
201 );
202 }
203
204 #[test]
205 fn test_name_fn_1_args() {
206 assert_eq!(
207 "Fn(Value) -> String",
208 Type::Fn {
209 args: vec![Type::Value],
210 variadic_arg: None,
211 returns: Type::String.into(),
212 }
213 .name()
214 );
215 }
216
217 #[test]
218 fn test_name_fn_2_args() {
219 assert_eq!(
220 "Fn(Value, String) -> String",
221 Type::Fn {
222 args: vec![Type::Value, Type::String],
223 variadic_arg: None,
224 returns: Type::String.into(),
225 }
226 .name()
227 );
228 }
229
230 #[test]
231 fn test_name_fn_2_args_and_varadic() {
232 assert_eq!(
233 "Fn(Value, String, ...Value) -> String",
234 Type::Fn {
235 args: vec![Type::Value, Type::String],
236 variadic_arg: Some(Type::Value.into()),
237 returns: Type::String.into(),
238 }
239 .name()
240 );
241 }
242
243 #[test]
244 fn test_name_bool() {
245 assert_eq!("Bool", Type::Bool.name());
246 }
247}