1use crate::lex::{Expression, Tensor};
21
22#[derive(Debug, Clone, PartialEq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Reference {
26 pub type_name: Option<String>,
28 pub id: String,
30}
31
32impl Reference {
33 pub fn local(id: impl Into<String>) -> Self {
35 Self {
36 type_name: None,
37 id: id.into(),
38 }
39 }
40
41 pub fn qualified(type_name: impl Into<String>, id: impl Into<String>) -> Self {
43 Self {
44 type_name: Some(type_name.into()),
45 id: id.into(),
46 }
47 }
48
49 pub fn to_ref_string(&self) -> String {
51 match &self.type_name {
52 Some(t) => format!("@{}:{}", t, self.id),
53 None => format!("@{}", self.id),
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq)]
60pub enum Value {
61 Null,
63 Bool(bool),
65 Int(i64),
67 Float(f64),
69 String(String),
71 Tensor(Tensor),
73 Reference(Reference),
75 Expression(Expression),
77}
78
79impl Value {
80 pub fn is_null(&self) -> bool {
82 matches!(self, Self::Null)
83 }
84
85 pub fn is_reference(&self) -> bool {
87 matches!(self, Self::Reference(_))
88 }
89
90 pub fn as_str(&self) -> Option<&str> {
92 match self {
93 Self::String(s) => Some(s),
94 _ => None,
95 }
96 }
97
98 pub fn as_int(&self) -> Option<i64> {
100 match self {
101 Self::Int(n) => Some(*n),
102 _ => None,
103 }
104 }
105
106 pub fn as_float(&self) -> Option<f64> {
108 match self {
109 Self::Float(n) => Some(*n),
110 Self::Int(n) => Some(*n as f64),
111 _ => None,
112 }
113 }
114
115 pub fn as_bool(&self) -> Option<bool> {
117 match self {
118 Self::Bool(b) => Some(*b),
119 _ => None,
120 }
121 }
122
123 pub fn as_reference(&self) -> Option<&Reference> {
125 match self {
126 Self::Reference(r) => Some(r),
127 _ => None,
128 }
129 }
130}
131
132impl std::fmt::Display for Value {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 match self {
135 Self::Null => write!(f, "~"),
136 Self::Bool(b) => write!(f, "{}", b),
137 Self::Int(n) => write!(f, "{}", n),
138 Self::Float(n) => write!(f, "{}", n),
139 Self::String(s) => write!(f, "{}", s),
140 Self::Tensor(_) => write!(f, "[tensor]"),
141 Self::Reference(r) => write!(f, "{}", r.to_ref_string()),
142 Self::Expression(e) => write!(f, "$({})", e),
143 }
144 }
145}
146
147impl Value {
148 pub fn as_expression(&self) -> Option<&Expression> {
150 match self {
151 Self::Expression(e) => Some(e),
152 _ => None,
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
164 fn test_reference_local() {
165 let r = Reference::local("user-123");
166 assert_eq!(r.type_name, None);
167 assert_eq!(r.id, "user-123");
168 }
169
170 #[test]
171 fn test_reference_qualified() {
172 let r = Reference::qualified("User", "123");
173 assert_eq!(r.type_name, Some("User".to_string()));
174 assert_eq!(r.id, "123");
175 }
176
177 #[test]
178 fn test_reference_to_ref_string_local() {
179 let r = Reference::local("id-1");
180 assert_eq!(r.to_ref_string(), "@id-1");
181 }
182
183 #[test]
184 fn test_reference_to_ref_string_qualified() {
185 let r = Reference::qualified("User", "id-1");
186 assert_eq!(r.to_ref_string(), "@User:id-1");
187 }
188
189 #[test]
190 fn test_reference_equality() {
191 let a = Reference::qualified("User", "1");
192 let b = Reference::qualified("User", "1");
193 assert_eq!(a, b);
194 }
195
196 #[test]
197 fn test_reference_inequality() {
198 let a = Reference::qualified("User", "1");
199 let b = Reference::qualified("Post", "1");
200 assert_ne!(a, b);
201 }
202
203 #[test]
204 fn test_reference_clone() {
205 let original = Reference::qualified("Type", "id");
206 let cloned = original.clone();
207 assert_eq!(original, cloned);
208 }
209
210 #[test]
211 fn test_reference_debug() {
212 let r = Reference::qualified("User", "abc");
213 let debug = format!("{:?}", r);
214 assert!(debug.contains("User"));
215 assert!(debug.contains("abc"));
216 }
217
218 #[test]
221 fn test_value_is_null() {
222 assert!(Value::Null.is_null());
223 assert!(!Value::Bool(true).is_null());
224 assert!(!Value::Int(0).is_null());
225 }
226
227 #[test]
228 fn test_value_is_reference() {
229 let r = Reference::local("id");
230 assert!(Value::Reference(r).is_reference());
231 assert!(!Value::Null.is_reference());
232 assert!(!Value::String("@ref".to_string()).is_reference());
233 }
234
235 #[test]
238 fn test_value_as_str() {
239 let v = Value::String("hello".to_string());
240 assert_eq!(v.as_str(), Some("hello"));
241 assert_eq!(Value::Null.as_str(), None);
242 assert_eq!(Value::Int(42).as_str(), None);
243 }
244
245 #[test]
246 fn test_value_as_int() {
247 assert_eq!(Value::Int(42).as_int(), Some(42));
248 assert_eq!(Value::Int(-100).as_int(), Some(-100));
249 assert_eq!(Value::Float(3.5).as_int(), None);
250 assert_eq!(Value::String("42".to_string()).as_int(), None);
251 }
252
253 #[test]
254 fn test_value_as_float() {
255 assert_eq!(Value::Float(3.5).as_float(), Some(3.5));
256 assert_eq!(Value::Int(42).as_float(), Some(42.0));
258 assert_eq!(Value::String("3.5".to_string()).as_float(), None);
259 }
260
261 #[test]
262 fn test_value_as_bool() {
263 assert_eq!(Value::Bool(true).as_bool(), Some(true));
264 assert_eq!(Value::Bool(false).as_bool(), Some(false));
265 assert_eq!(Value::Int(1).as_bool(), None);
266 assert_eq!(Value::String("true".to_string()).as_bool(), None);
267 }
268
269 #[test]
270 fn test_value_as_reference() {
271 let r = Reference::local("id");
272 let v = Value::Reference(r.clone());
273 assert_eq!(v.as_reference(), Some(&r));
274 assert_eq!(Value::Null.as_reference(), None);
275 }
276
277 #[test]
278 fn test_value_as_expression() {
279 use crate::lex::{Expression, Span};
280 let expr = Expression::Identifier {
281 name: "x".to_string(),
282 span: Span::default(),
283 };
284 let v = Value::Expression(expr.clone());
285 assert_eq!(v.as_expression(), Some(&expr));
286 assert_eq!(Value::Null.as_expression(), None);
287 }
288
289 #[test]
292 fn test_value_display_null() {
293 assert_eq!(format!("{}", Value::Null), "~");
294 }
295
296 #[test]
297 fn test_value_display_bool() {
298 assert_eq!(format!("{}", Value::Bool(true)), "true");
299 assert_eq!(format!("{}", Value::Bool(false)), "false");
300 }
301
302 #[test]
303 fn test_value_display_int() {
304 assert_eq!(format!("{}", Value::Int(42)), "42");
305 assert_eq!(format!("{}", Value::Int(-100)), "-100");
306 assert_eq!(format!("{}", Value::Int(0)), "0");
307 }
308
309 #[test]
310 fn test_value_display_float() {
311 let s = format!("{}", Value::Float(3.5));
312 assert!(s.starts_with("3.5"));
313 }
314
315 #[test]
316 fn test_value_display_string() {
317 assert_eq!(format!("{}", Value::String("hello".to_string())), "hello");
318 }
319
320 #[test]
321 fn test_value_display_reference() {
322 let r = Reference::qualified("User", "123");
323 assert_eq!(format!("{}", Value::Reference(r)), "@User:123");
324 }
325
326 #[test]
327 fn test_value_display_expression() {
328 use crate::lex::{Expression, Span};
329 let expr = Expression::Identifier {
330 name: "x".to_string(),
331 span: Span::default(),
332 };
333 assert_eq!(format!("{}", Value::Expression(expr)), "$(x)");
334 }
335
336 #[test]
337 fn test_value_display_tensor() {
338 use crate::lex::Tensor;
339 let t = Tensor::Array(vec![Tensor::Scalar(1.0), Tensor::Scalar(2.0)]);
340 assert_eq!(format!("{}", Value::Tensor(t)), "[tensor]");
341 }
342
343 #[test]
346 fn test_value_equality_null() {
347 assert_eq!(Value::Null, Value::Null);
348 }
349
350 #[test]
351 fn test_value_equality_bool() {
352 assert_eq!(Value::Bool(true), Value::Bool(true));
353 assert_ne!(Value::Bool(true), Value::Bool(false));
354 }
355
356 #[test]
357 fn test_value_equality_int() {
358 assert_eq!(Value::Int(42), Value::Int(42));
359 assert_ne!(Value::Int(42), Value::Int(43));
360 }
361
362 #[test]
363 fn test_value_equality_string() {
364 assert_eq!(
365 Value::String("test".to_string()),
366 Value::String("test".to_string())
367 );
368 assert_ne!(
369 Value::String("a".to_string()),
370 Value::String("b".to_string())
371 );
372 }
373
374 #[test]
375 fn test_value_inequality_different_types() {
376 assert_ne!(Value::Int(1), Value::Bool(true));
377 assert_ne!(Value::Null, Value::Bool(false));
378 assert_ne!(Value::String("42".to_string()), Value::Int(42));
379 }
380
381 #[test]
382 fn test_value_clone() {
383 let values = vec![
384 Value::Null,
385 Value::Bool(true),
386 Value::Int(42),
387 Value::Float(3.5),
388 Value::String("test".to_string()),
389 Value::Reference(Reference::local("id")),
390 ];
391
392 for v in values {
393 let cloned = v.clone();
394 assert_eq!(v, cloned);
395 }
396 }
397
398 #[test]
399 fn test_value_debug() {
400 let v = Value::Int(42);
401 let debug = format!("{:?}", v);
402 assert!(debug.contains("Int"));
403 assert!(debug.contains("42"));
404 }
405
406 #[test]
409 fn test_value_int_bounds() {
410 assert_eq!(Value::Int(i64::MAX).as_int(), Some(i64::MAX));
411 assert_eq!(Value::Int(i64::MIN).as_int(), Some(i64::MIN));
412 }
413
414 #[test]
415 fn test_value_empty_string() {
416 let v = Value::String(String::new());
417 assert_eq!(v.as_str(), Some(""));
418 }
419
420 #[test]
421 fn test_value_unicode_string() {
422 let v = Value::String("日本語 🎉".to_string());
423 assert_eq!(v.as_str(), Some("日本語 🎉"));
424 }
425
426 #[test]
427 fn test_value_float_special() {
428 let inf = Value::Float(f64::INFINITY);
429 assert!(inf.as_float().unwrap().is_infinite());
430
431 let nan = Value::Float(f64::NAN);
432 assert!(nan.as_float().unwrap().is_nan());
433 }
434
435 #[test]
436 fn test_reference_empty_id() {
437 let r = Reference::local("");
438 assert_eq!(r.id, "");
439 assert_eq!(r.to_ref_string(), "@");
440 }
441
442 #[test]
443 fn test_reference_with_special_chars() {
444 let r = Reference::local("id-with-hyphens-123");
445 assert_eq!(r.to_ref_string(), "@id-with-hyphens-123");
446 }
447}