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