darklua_core/process/evaluator/
lua_value.rs1use crate::nodes::{Expression, NumberExpression, StringExpression};
2
3#[derive(Debug, Clone, PartialEq)]
5pub enum LuaValue {
6 False,
7 Function,
8 Nil,
9 Number(f64),
10 String(String),
11 Table,
12 True,
13 Unknown,
14}
15
16impl LuaValue {
17 pub fn is_truthy(&self) -> Option<bool> {
36 match self {
37 Self::Unknown => None,
38 Self::Nil | Self::False => Some(false),
39 _ => Some(true),
40 }
41 }
42
43 pub fn map_if_truthy<F>(self, map: F) -> Self
47 where
48 F: Fn(Self) -> Self,
49 {
50 match self.is_truthy() {
51 Some(true) => map(self),
52 Some(false) => self,
53 _ => Self::Unknown,
54 }
55 }
56
57 pub fn map_if_truthy_else<F, G>(self, map: F, default: G) -> Self
60 where
61 F: Fn(Self) -> Self,
62 G: Fn() -> Self,
63 {
64 match self.is_truthy() {
65 Some(true) => map(self),
66 Some(false) => default(),
67 _ => Self::Unknown,
68 }
69 }
70
71 pub fn to_expression(self) -> Option<Expression> {
73 match self {
74 Self::False => Some(Expression::from(false)),
75 Self::True => Some(Expression::from(true)),
76 Self::Nil => Some(Expression::nil()),
77 Self::String(value) => Some(StringExpression::from_value(value).into()),
78 Self::Number(value) => Some(Expression::from(value)),
79 _ => None,
80 }
81 }
82
83 pub fn number_coercion(self) -> Self {
86 match &self {
87 Self::String(string) => {
88 let string = string.trim();
89
90 let number = if string.starts_with('-') {
91 string
92 .get(1..)
93 .and_then(|string| string.parse::<NumberExpression>().ok())
94 .map(|number| number.compute_value() * -1.0)
95 } else {
96 string
97 .parse::<NumberExpression>()
98 .ok()
99 .map(|number| number.compute_value())
100 };
101
102 number.map(LuaValue::Number)
103 }
104 _ => None,
105 }
106 .unwrap_or(self)
107 }
108
109 pub fn string_coercion(self) -> Self {
112 match &self {
113 Self::Number(value) => Some(Self::String(format!("{}", value))),
114 _ => None,
115 }
116 .unwrap_or(self)
117 }
118}
119
120impl Default for LuaValue {
121 fn default() -> Self {
122 Self::Unknown
123 }
124}
125
126impl From<bool> for LuaValue {
127 fn from(value: bool) -> Self {
128 if value {
129 Self::True
130 } else {
131 Self::False
132 }
133 }
134}
135
136impl From<String> for LuaValue {
137 fn from(value: String) -> Self {
138 Self::String(value)
139 }
140}
141
142impl From<&str> for LuaValue {
143 fn from(value: &str) -> Self {
144 Self::String(value.to_owned())
145 }
146}
147
148impl From<f64> for LuaValue {
149 fn from(value: f64) -> Self {
150 Self::Number(value)
151 }
152}
153
154#[cfg(test)]
155mod test {
156 use super::*;
157
158 #[test]
159 fn unknown_lua_value_is_truthy_returns_none() {
160 assert!(LuaValue::Unknown.is_truthy().is_none());
161 }
162
163 #[test]
164 fn false_value_is_not_truthy() {
165 assert!(!LuaValue::False.is_truthy().unwrap());
166 }
167
168 #[test]
169 fn nil_value_is_not_truthy() {
170 assert!(!LuaValue::Nil.is_truthy().unwrap());
171 }
172
173 #[test]
174 fn true_value_is_truthy() {
175 assert!(LuaValue::True.is_truthy().unwrap());
176 }
177
178 #[test]
179 fn zero_value_is_truthy() {
180 assert!(LuaValue::Number(0_f64).is_truthy().unwrap());
181 }
182
183 #[test]
184 fn string_value_is_truthy() {
185 assert!(LuaValue::String("".to_owned()).is_truthy().unwrap());
186 }
187
188 #[test]
189 fn table_value_is_truthy() {
190 assert!(LuaValue::Table.is_truthy().unwrap());
191 }
192
193 mod number_coercion {
194 use super::*;
195
196 macro_rules! number_coercion {
197 ($($name:ident ($string:literal) => $result:expr),*) => {
198 $(
199 #[test]
200 fn $name() {
201 assert_eq!(
202 LuaValue::String($string.into()).number_coercion(),
203 LuaValue::Number($result)
204 );
205 }
206 )*
207 };
208 }
209
210 macro_rules! no_number_coercion {
211 ($($name:ident ($string:literal)),*) => {
212 $(
213 #[test]
214 fn $name() {
215 assert_eq!(
216 LuaValue::String($string.into()).number_coercion(),
217 LuaValue::String($string.into())
218 );
219 }
220 )*
221 };
222 }
223
224 number_coercion!(
225 zero("0") => 0.0,
226 integer("12") => 12.0,
227 integer_with_leading_zeros("00012") => 12.0,
228 integer_with_ending_space("12 ") => 12.0,
229 integer_with_leading_space(" 123") => 123.0,
230 integer_with_leading_tab("\t123") => 123.0,
231 negative_integer("-3") => -3.0,
232 hex_zero("0x0") => 0.0,
233 hex_integer("0xA") => 10.0,
234 negative_hex_integer("-0xA") => -10.0,
235 float("0.5") => 0.5,
236 negative_float("-0.5") => -0.5,
237 float_starting_with_dot(".5") => 0.5
238 );
239
240 no_number_coercion!(
241 letter_suffix("123a"),
242 hex_prefix("0x"),
243 space_between_minus("- 1"),
244 two_seperated_digits(" 1 2")
245 );
246 }
247}