1use std::fmt::Display;
4
5use crate::{
6 builtins::BuiltinFn,
7 errors::{ExprResult, RuntimeError},
8 types::Type,
9};
10
11#[derive(Debug, Clone, PartialEq)]
12pub enum Value {
13 String(String),
14 Number(f64),
15 Fn(Box<BuiltinFn<'static>>),
16 Bool(bool),
17 Type(Box<Type>),
18}
19
20impl Value {
21 pub fn get_type(&self) -> Type {
22 self.clone().into()
23 }
24
25 pub fn get_string(&self) -> ExprResult<&str> {
26 match self {
27 Value::String(s) => Ok(s.as_str()),
28 _ => Err(vec![(
29 RuntimeError::TypeMismatch {
30 expected: Type::String,
31 actual: self.get_type(),
32 }
33 .into(),
34 0..0,
35 )]),
36 }
37 }
38
39 pub fn get_func(&self) -> ExprResult<Box<BuiltinFn<'_>>> {
40 match self {
41 Value::Fn(f) => Ok(f.clone()),
42 _ => Err(vec![(
43 RuntimeError::TypeMismatch {
44 expected: Type::Fn {
45 args: vec![],
46 variadic_arg: Some(Type::Value.into()),
47 returns: Type::Value.into(),
48 },
49 actual: self.get_type(),
50 }
51 .into(),
52 0..0,
53 )]),
54 }
55 }
56
57 pub fn get_bool(&self) -> ExprResult<bool> {
58 match self {
59 Value::Bool(s) => Ok(*s),
60 _ => Err(vec![(
61 RuntimeError::TypeMismatch {
62 expected: Type::Bool,
63 actual: self.get_type(),
64 }
65 .into(),
66 0..0,
67 )]),
68 }
69 }
70}
71
72impl Display for Value {
73 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
74 match self {
75 Value::String(string) => write!(f, "`{string}`"),
76 Value::Number(value) => write!(f, "{value}"),
77 Value::Fn(builtin) => write!(f, "{builtin:?}"),
78 Value::Bool(value) => write!(f, "{value}"),
79 Value::Type(ty) => write!(f, "Type<{ty}>"),
80 }
81 }
82}
83
84impl From<bool> for Value {
85 fn from(value: bool) -> Self {
86 Self::Bool(value)
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 use pretty_assertions::assert_eq;
95
96 fn example_builtin(_args: Vec<Value>) -> ExprResult<Value> {
97 Ok(Value::String("".to_string()))
98 }
99
100 #[test]
101 fn get_bool_on_string() {
102 assert_eq!(
103 Err(vec![(
104 RuntimeError::TypeMismatch {
105 expected: Type::Bool,
106 actual: Type::String
107 }
108 .into(),
109 0..0
110 )]),
111 Value::String("string".to_string()).get_bool()
112 );
113 }
114
115 #[test]
116 fn get_bool_on_bool_true() {
117 assert_eq!(Ok(true), Value::Bool(true).get_bool());
118 }
119
120 #[test]
121 fn get_string_on_bool() {
122 assert_eq!(
123 Err(vec![(
124 RuntimeError::TypeMismatch {
125 expected: Type::String,
126 actual: Type::Bool
127 }
128 .into(),
129 0..0
130 )]),
131 Value::Bool(true).get_string()
132 );
133 }
134
135 #[test]
136 fn get_string_on_string() {
137 let value = Value::String("test".to_string());
138 assert_eq!(Ok("test"), value.get_string());
139 }
140
141 #[test]
142 fn get_func_on_string() {
143 let value = Value::String("not a function".to_string());
144 assert_eq!(
145 Err(vec![(
146 RuntimeError::TypeMismatch {
147 expected: Type::Fn {
148 args: vec![],
149 variadic_arg: Some(Type::Value.into()),
150 returns: Type::Value.into()
151 },
152 actual: Type::String
153 }
154 .into(),
155 0..0
156 )]),
157 value.get_func()
158 );
159 }
160
161 #[test]
162 fn get_func_on_func() {
163 let expected_fn: Box<BuiltinFn> = BuiltinFn {
164 name: "name",
165 args: &[],
166 return_type: Type::Unknown,
167 func: example_builtin,
168 }
169 .into();
170
171 let value = Value::Fn(expected_fn.clone());
172
173 assert_eq!(Ok(expected_fn), value.get_func());
174 }
175
176 #[test]
177 fn get_func_on_bool() {
178 let value = Value::Bool(true);
179 assert_eq!(
180 Err(vec![(
181 RuntimeError::TypeMismatch {
182 expected: Type::Fn {
183 args: vec![],
184 variadic_arg: Some(Type::Value.into()),
185 returns: Type::Value.into()
186 },
187 actual: Type::Bool
188 }
189 .into(),
190 0..0
191 )]),
192 value.get_func()
193 );
194 }
195}