1use crate::lex::{Expression, Tensor};
21
22#[derive(Debug, Clone, PartialEq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct Reference {
31 pub type_name: Option<Box<str>>,
34 pub id: Box<str>,
37}
38
39impl Reference {
40 pub fn local(id: impl Into<String>) -> Self {
42 Self {
43 type_name: None,
44 id: id.into().into_boxed_str(),
45 }
46 }
47
48 pub fn qualified(type_name: impl Into<String>, id: impl Into<String>) -> Self {
50 Self {
51 type_name: Some(type_name.into().into_boxed_str()),
52 id: id.into().into_boxed_str(),
53 }
54 }
55
56 pub fn unqualified(id: impl Into<String>) -> Self {
62 Self::local(id)
63 }
64
65 pub fn to_ref_string(&self) -> String {
67 match &self.type_name {
68 Some(t) => format!("@{}:{}", t, self.id),
69 None => format!("@{}", self.id),
70 }
71 }
72}
73
74#[derive(Debug, Clone, PartialEq)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub enum Value {
84 Null,
86 Bool(bool),
88 Int(i64),
90 Float(f64),
92 String(Box<str>),
94 Tensor(Box<Tensor>),
96 Reference(Reference),
98 Expression(Box<Expression>),
100 List(Box<Vec<Value>>),
128}
129
130impl Value {
131 pub fn is_null(&self) -> bool {
133 matches!(self, Self::Null)
134 }
135
136 pub fn is_reference(&self) -> bool {
138 matches!(self, Self::Reference(_))
139 }
140
141 pub fn is_list(&self) -> bool {
143 matches!(self, Self::List(_))
144 }
145
146 pub fn as_list(&self) -> Option<&[Value]> {
148 match self {
149 Self::List(l) => Some(l),
150 _ => None,
151 }
152 }
153
154 pub fn as_str(&self) -> Option<&str> {
156 match self {
157 Self::String(s) => Some(s),
158 _ => None,
159 }
160 }
161
162 pub fn as_int(&self) -> Option<i64> {
164 match self {
165 Self::Int(n) => Some(*n),
166 _ => None,
167 }
168 }
169
170 pub fn as_float(&self) -> Option<f64> {
172 match self {
173 Self::Float(n) => Some(*n),
174 Self::Int(n) => Some(*n as f64),
175 _ => None,
176 }
177 }
178
179 pub fn as_bool(&self) -> Option<bool> {
181 match self {
182 Self::Bool(b) => Some(*b),
183 _ => None,
184 }
185 }
186
187 pub fn as_reference(&self) -> Option<&Reference> {
189 match self {
190 Self::Reference(r) => Some(r),
191 _ => None,
192 }
193 }
194}
195
196impl std::fmt::Display for Value {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 match self {
199 Self::Null => write!(f, "~"),
200 Self::Bool(b) => write!(f, "{}", b),
201 Self::Int(n) => write!(f, "{}", n),
202 Self::Float(n) => write!(f, "{}", n),
203 Self::String(s) => write!(f, "{}", s),
204 Self::Tensor(_) => write!(f, "[tensor]"),
205 Self::Reference(r) => write!(f, "{}", r.to_ref_string()),
206 Self::Expression(e) => write!(f, "$({})", e),
207 Self::List(items) => {
208 write!(f, "(")?;
209 for (i, item) in items.iter().enumerate() {
210 if i > 0 {
211 write!(f, ", ")?;
212 }
213 write!(f, "{}", item)?;
214 }
215 write!(f, ")")
216 }
217 }
218 }
219}
220
221impl Value {
222 pub fn as_expression(&self) -> Option<&Expression> {
224 match self {
225 Self::Expression(e) => Some(e),
226 _ => None,
227 }
228 }
229
230 pub fn coerce_to(
245 &self,
246 expected: &crate::types::ExpectedType,
247 ) -> crate::coercion::CoercionResult {
248 crate::coercion::coerce(
249 self.clone(),
250 expected,
251 crate::coercion::CoercionMode::Lenient,
252 )
253 }
254
255 pub fn can_coerce_to(&self, expected: &crate::types::ExpectedType) -> bool {
272 !matches!(
273 self.coerce_to(expected),
274 crate::coercion::CoercionResult::Failed { .. }
275 )
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 #[test]
286 fn test_reference_local() {
287 let r = Reference::local("user-123");
288 assert_eq!(r.type_name, None);
289 assert_eq!(r.id.as_ref(), "user-123");
290 }
291
292 #[test]
293 fn test_reference_qualified() {
294 let r = Reference::qualified("User", "123");
295 assert_eq!(r.type_name.as_deref(), Some("User"));
296 assert_eq!(r.id.as_ref(), "123");
297 }
298
299 #[test]
300 fn test_reference_to_ref_string_local() {
301 let r = Reference::local("id-1");
302 assert_eq!(r.to_ref_string(), "@id-1");
303 }
304
305 #[test]
306 fn test_reference_to_ref_string_qualified() {
307 let r = Reference::qualified("User", "id-1");
308 assert_eq!(r.to_ref_string(), "@User:id-1");
309 }
310
311 #[test]
312 fn test_reference_equality() {
313 let a = Reference::qualified("User", "1");
314 let b = Reference::qualified("User", "1");
315 assert_eq!(a, b);
316 }
317
318 #[test]
319 fn test_reference_inequality() {
320 let a = Reference::qualified("User", "1");
321 let b = Reference::qualified("Post", "1");
322 assert_ne!(a, b);
323 }
324
325 #[test]
326 fn test_reference_clone() {
327 let original = Reference::qualified("Type", "id");
328 let cloned = original.clone();
329 assert_eq!(original, cloned);
330 }
331
332 #[test]
333 fn test_reference_debug() {
334 let r = Reference::qualified("User", "abc");
335 let debug = format!("{:?}", r);
336 assert!(debug.contains("User"));
337 assert!(debug.contains("abc"));
338 }
339
340 #[test]
343 fn test_value_is_null() {
344 assert!(Value::Null.is_null());
345 assert!(!Value::Bool(true).is_null());
346 assert!(!Value::Int(0).is_null());
347 }
348
349 #[test]
350 fn test_value_is_reference() {
351 let r = Reference::local("id");
352 assert!(Value::Reference(r).is_reference());
353 assert!(!Value::Null.is_reference());
354 assert!(!Value::String("@ref".to_string().into()).is_reference());
355 }
356
357 #[test]
360 fn test_value_as_str() {
361 let v = Value::String("hello".to_string().into());
362 assert_eq!(v.as_str(), Some("hello"));
363 assert_eq!(Value::Null.as_str(), None);
364 assert_eq!(Value::Int(42).as_str(), None);
365 }
366
367 #[test]
368 fn test_value_as_int() {
369 assert_eq!(Value::Int(42).as_int(), Some(42));
370 assert_eq!(Value::Int(-100).as_int(), Some(-100));
371 assert_eq!(Value::Float(3.5).as_int(), None);
372 assert_eq!(Value::String("42".to_string().into()).as_int(), None);
373 }
374
375 #[test]
376 fn test_value_as_float() {
377 assert_eq!(Value::Float(3.5).as_float(), Some(3.5));
378 assert_eq!(Value::Int(42).as_float(), Some(42.0));
380 assert_eq!(Value::String("3.5".to_string().into()).as_float(), None);
381 }
382
383 #[test]
384 fn test_value_as_bool() {
385 assert_eq!(Value::Bool(true).as_bool(), Some(true));
386 assert_eq!(Value::Bool(false).as_bool(), Some(false));
387 assert_eq!(Value::Int(1).as_bool(), None);
388 assert_eq!(Value::String("true".to_string().into()).as_bool(), None);
389 }
390
391 #[test]
392 fn test_value_as_reference() {
393 let r = Reference::local("id");
394 let v = Value::Reference(r.clone());
395 assert_eq!(v.as_reference(), Some(&r));
396 assert_eq!(Value::Null.as_reference(), None);
397 }
398
399 #[test]
400 fn test_value_as_expression() {
401 use crate::lex::{Expression, Span};
402 let expr = Expression::Identifier {
403 name: "x".to_string(),
404 span: Span::synthetic(),
405 };
406 let v = Value::Expression(Box::new(expr.clone()));
407 assert_eq!(v.as_expression(), Some(&expr));
408 assert_eq!(Value::Null.as_expression(), None);
409 }
410
411 #[test]
414 fn test_value_display_null() {
415 assert_eq!(format!("{}", Value::Null), "~");
416 }
417
418 #[test]
419 fn test_value_display_bool() {
420 assert_eq!(format!("{}", Value::Bool(true)), "true");
421 assert_eq!(format!("{}", Value::Bool(false)), "false");
422 }
423
424 #[test]
425 fn test_value_display_int() {
426 assert_eq!(format!("{}", Value::Int(42)), "42");
427 assert_eq!(format!("{}", Value::Int(-100)), "-100");
428 assert_eq!(format!("{}", Value::Int(0)), "0");
429 }
430
431 #[test]
432 fn test_value_display_float() {
433 let s = format!("{}", Value::Float(3.5));
434 assert!(s.starts_with("3.5"));
435 }
436
437 #[test]
438 fn test_value_display_string() {
439 assert_eq!(
440 format!("{}", Value::String("hello".to_string().into())),
441 "hello"
442 );
443 }
444
445 #[test]
446 fn test_value_display_reference() {
447 let r = Reference::qualified("User", "123");
448 assert_eq!(format!("{}", Value::Reference(r)), "@User:123");
449 }
450
451 #[test]
452 fn test_value_display_expression() {
453 use crate::lex::{Expression, Span};
454 let expr = Expression::Identifier {
455 name: "x".to_string(),
456 span: Span::synthetic(),
457 };
458 assert_eq!(format!("{}", Value::Expression(Box::new(expr))), "$(x)");
459 }
460
461 #[test]
462 fn test_value_display_tensor() {
463 use crate::lex::Tensor;
464 let t = Tensor::Array(vec![Tensor::Scalar(1.0), Tensor::Scalar(2.0)]);
465 assert_eq!(format!("{}", Value::Tensor(Box::new(t))), "[tensor]");
466 }
467
468 #[test]
471 fn test_value_equality_null() {
472 assert_eq!(Value::Null, Value::Null);
473 }
474
475 #[test]
476 fn test_value_equality_bool() {
477 assert_eq!(Value::Bool(true), Value::Bool(true));
478 assert_ne!(Value::Bool(true), Value::Bool(false));
479 }
480
481 #[test]
482 fn test_value_equality_int() {
483 assert_eq!(Value::Int(42), Value::Int(42));
484 assert_ne!(Value::Int(42), Value::Int(43));
485 }
486
487 #[test]
488 fn test_value_equality_string() {
489 assert_eq!(
490 Value::String("test".to_string().into()),
491 Value::String("test".to_string().into())
492 );
493 assert_ne!(
494 Value::String("a".to_string().into()),
495 Value::String("b".to_string().into())
496 );
497 }
498
499 #[test]
500 fn test_value_inequality_different_types() {
501 assert_ne!(Value::Int(1), Value::Bool(true));
502 assert_ne!(Value::Null, Value::Bool(false));
503 assert_ne!(Value::String("42".to_string().into()), Value::Int(42));
504 }
505
506 #[test]
507 fn test_value_clone() {
508 let values = vec![
509 Value::Null,
510 Value::Bool(true),
511 Value::Int(42),
512 Value::Float(3.5),
513 Value::String("test".to_string().into()),
514 Value::Reference(Reference::local("id")),
515 ];
516
517 for v in values {
518 let cloned = v.clone();
519 assert_eq!(v, cloned);
520 }
521 }
522
523 #[test]
524 fn test_value_debug() {
525 let v = Value::Int(42);
526 let debug = format!("{:?}", v);
527 assert!(debug.contains("Int"));
528 assert!(debug.contains("42"));
529 }
530
531 #[test]
534 fn test_value_int_bounds() {
535 assert_eq!(Value::Int(i64::MAX).as_int(), Some(i64::MAX));
536 assert_eq!(Value::Int(i64::MIN).as_int(), Some(i64::MIN));
537 }
538
539 #[test]
540 fn test_value_empty_string() {
541 let v = Value::String(String::new().into());
542 assert_eq!(v.as_str(), Some(""));
543 }
544
545 #[test]
546 fn test_value_unicode_string() {
547 let v = Value::String("日本語 🎉".to_string().into());
548 assert_eq!(v.as_str(), Some("日本語 🎉"));
549 }
550
551 #[test]
552 fn test_value_float_special() {
553 let inf = Value::Float(f64::INFINITY);
554 assert!(inf.as_float().unwrap().is_infinite());
555
556 let nan = Value::Float(f64::NAN);
557 assert!(nan.as_float().unwrap().is_nan());
558 }
559
560 #[test]
561 fn test_reference_empty_id() {
562 let r = Reference::local("");
563 assert_eq!(r.id.as_ref(), "");
564 assert_eq!(r.to_ref_string(), "@");
565 }
566
567 #[test]
568 fn test_reference_with_special_chars() {
569 let r = Reference::local("id-with-hyphens-123");
570 assert_eq!(r.to_ref_string(), "@id-with-hyphens-123");
571 }
572}