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)]
82pub enum Value {
83 Null,
85 Bool(bool),
87 Int(i64),
89 Float(f64),
91 String(Box<str>),
93 Tensor(Box<Tensor>),
95 Reference(Reference),
97 Expression(Box<Expression>),
99}
100
101impl Value {
102 pub fn is_null(&self) -> bool {
104 matches!(self, Self::Null)
105 }
106
107 pub fn is_reference(&self) -> bool {
109 matches!(self, Self::Reference(_))
110 }
111
112 pub fn as_str(&self) -> Option<&str> {
114 match self {
115 Self::String(s) => Some(s),
116 _ => None,
117 }
118 }
119
120 pub fn as_int(&self) -> Option<i64> {
122 match self {
123 Self::Int(n) => Some(*n),
124 _ => None,
125 }
126 }
127
128 pub fn as_float(&self) -> Option<f64> {
130 match self {
131 Self::Float(n) => Some(*n),
132 Self::Int(n) => Some(*n as f64),
133 _ => None,
134 }
135 }
136
137 pub fn as_bool(&self) -> Option<bool> {
139 match self {
140 Self::Bool(b) => Some(*b),
141 _ => None,
142 }
143 }
144
145 pub fn as_reference(&self) -> Option<&Reference> {
147 match self {
148 Self::Reference(r) => Some(r),
149 _ => None,
150 }
151 }
152}
153
154impl std::fmt::Display for Value {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 Self::Null => write!(f, "~"),
158 Self::Bool(b) => write!(f, "{}", b),
159 Self::Int(n) => write!(f, "{}", n),
160 Self::Float(n) => write!(f, "{}", n),
161 Self::String(s) => write!(f, "{}", s),
162 Self::Tensor(_) => write!(f, "[tensor]"),
163 Self::Reference(r) => write!(f, "{}", r.to_ref_string()),
164 Self::Expression(e) => write!(f, "$({})", e),
165 }
166 }
167}
168
169impl Value {
170 pub fn as_expression(&self) -> Option<&Expression> {
172 match self {
173 Self::Expression(e) => Some(e),
174 _ => None,
175 }
176 }
177
178 pub fn coerce_to(
193 &self,
194 expected: &crate::types::ExpectedType,
195 ) -> crate::coercion::CoercionResult {
196 crate::coercion::coerce(
197 self.clone(),
198 expected,
199 crate::coercion::CoercionMode::Lenient,
200 )
201 }
202
203 pub fn can_coerce_to(&self, expected: &crate::types::ExpectedType) -> bool {
220 !matches!(
221 self.coerce_to(expected),
222 crate::coercion::CoercionResult::Failed { .. }
223 )
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
234 fn test_reference_local() {
235 let r = Reference::local("user-123");
236 assert_eq!(r.type_name, None);
237 assert_eq!(r.id.as_ref(), "user-123");
238 }
239
240 #[test]
241 fn test_reference_qualified() {
242 let r = Reference::qualified("User", "123");
243 assert_eq!(r.type_name.as_deref(), Some("User"));
244 assert_eq!(r.id.as_ref(), "123");
245 }
246
247 #[test]
248 fn test_reference_to_ref_string_local() {
249 let r = Reference::local("id-1");
250 assert_eq!(r.to_ref_string(), "@id-1");
251 }
252
253 #[test]
254 fn test_reference_to_ref_string_qualified() {
255 let r = Reference::qualified("User", "id-1");
256 assert_eq!(r.to_ref_string(), "@User:id-1");
257 }
258
259 #[test]
260 fn test_reference_equality() {
261 let a = Reference::qualified("User", "1");
262 let b = Reference::qualified("User", "1");
263 assert_eq!(a, b);
264 }
265
266 #[test]
267 fn test_reference_inequality() {
268 let a = Reference::qualified("User", "1");
269 let b = Reference::qualified("Post", "1");
270 assert_ne!(a, b);
271 }
272
273 #[test]
274 fn test_reference_clone() {
275 let original = Reference::qualified("Type", "id");
276 let cloned = original.clone();
277 assert_eq!(original, cloned);
278 }
279
280 #[test]
281 fn test_reference_debug() {
282 let r = Reference::qualified("User", "abc");
283 let debug = format!("{:?}", r);
284 assert!(debug.contains("User"));
285 assert!(debug.contains("abc"));
286 }
287
288 #[test]
291 fn test_value_is_null() {
292 assert!(Value::Null.is_null());
293 assert!(!Value::Bool(true).is_null());
294 assert!(!Value::Int(0).is_null());
295 }
296
297 #[test]
298 fn test_value_is_reference() {
299 let r = Reference::local("id");
300 assert!(Value::Reference(r).is_reference());
301 assert!(!Value::Null.is_reference());
302 assert!(!Value::String("@ref".to_string().into()).is_reference());
303 }
304
305 #[test]
308 fn test_value_as_str() {
309 let v = Value::String("hello".to_string().into());
310 assert_eq!(v.as_str(), Some("hello"));
311 assert_eq!(Value::Null.as_str(), None);
312 assert_eq!(Value::Int(42).as_str(), None);
313 }
314
315 #[test]
316 fn test_value_as_int() {
317 assert_eq!(Value::Int(42).as_int(), Some(42));
318 assert_eq!(Value::Int(-100).as_int(), Some(-100));
319 assert_eq!(Value::Float(3.5).as_int(), None);
320 assert_eq!(Value::String("42".to_string().into()).as_int(), None);
321 }
322
323 #[test]
324 fn test_value_as_float() {
325 assert_eq!(Value::Float(3.5).as_float(), Some(3.5));
326 assert_eq!(Value::Int(42).as_float(), Some(42.0));
328 assert_eq!(Value::String("3.5".to_string().into()).as_float(), None);
329 }
330
331 #[test]
332 fn test_value_as_bool() {
333 assert_eq!(Value::Bool(true).as_bool(), Some(true));
334 assert_eq!(Value::Bool(false).as_bool(), Some(false));
335 assert_eq!(Value::Int(1).as_bool(), None);
336 assert_eq!(Value::String("true".to_string().into()).as_bool(), None);
337 }
338
339 #[test]
340 fn test_value_as_reference() {
341 let r = Reference::local("id");
342 let v = Value::Reference(r.clone());
343 assert_eq!(v.as_reference(), Some(&r));
344 assert_eq!(Value::Null.as_reference(), None);
345 }
346
347 #[test]
348 fn test_value_as_expression() {
349 use crate::lex::{Expression, Span};
350 let expr = Expression::Identifier {
351 name: "x".to_string(),
352 span: Span::synthetic(),
353 };
354 let v = Value::Expression(Box::new(expr.clone()));
355 assert_eq!(v.as_expression(), Some(&expr));
356 assert_eq!(Value::Null.as_expression(), None);
357 }
358
359 #[test]
362 fn test_value_display_null() {
363 assert_eq!(format!("{}", Value::Null), "~");
364 }
365
366 #[test]
367 fn test_value_display_bool() {
368 assert_eq!(format!("{}", Value::Bool(true)), "true");
369 assert_eq!(format!("{}", Value::Bool(false)), "false");
370 }
371
372 #[test]
373 fn test_value_display_int() {
374 assert_eq!(format!("{}", Value::Int(42)), "42");
375 assert_eq!(format!("{}", Value::Int(-100)), "-100");
376 assert_eq!(format!("{}", Value::Int(0)), "0");
377 }
378
379 #[test]
380 fn test_value_display_float() {
381 let s = format!("{}", Value::Float(3.5));
382 assert!(s.starts_with("3.5"));
383 }
384
385 #[test]
386 fn test_value_display_string() {
387 assert_eq!(
388 format!("{}", Value::String("hello".to_string().into())),
389 "hello"
390 );
391 }
392
393 #[test]
394 fn test_value_display_reference() {
395 let r = Reference::qualified("User", "123");
396 assert_eq!(format!("{}", Value::Reference(r)), "@User:123");
397 }
398
399 #[test]
400 fn test_value_display_expression() {
401 use crate::lex::{Expression, Span};
402 let expr = Expression::Identifier {
403 name: "x".to_string(),
404 span: Span::synthetic(),
405 };
406 assert_eq!(format!("{}", Value::Expression(Box::new(expr))), "$(x)");
407 }
408
409 #[test]
410 fn test_value_display_tensor() {
411 use crate::lex::Tensor;
412 let t = Tensor::Array(vec![Tensor::Scalar(1.0), Tensor::Scalar(2.0)]);
413 assert_eq!(format!("{}", Value::Tensor(Box::new(t))), "[tensor]");
414 }
415
416 #[test]
419 fn test_value_equality_null() {
420 assert_eq!(Value::Null, Value::Null);
421 }
422
423 #[test]
424 fn test_value_equality_bool() {
425 assert_eq!(Value::Bool(true), Value::Bool(true));
426 assert_ne!(Value::Bool(true), Value::Bool(false));
427 }
428
429 #[test]
430 fn test_value_equality_int() {
431 assert_eq!(Value::Int(42), Value::Int(42));
432 assert_ne!(Value::Int(42), Value::Int(43));
433 }
434
435 #[test]
436 fn test_value_equality_string() {
437 assert_eq!(
438 Value::String("test".to_string().into()),
439 Value::String("test".to_string().into())
440 );
441 assert_ne!(
442 Value::String("a".to_string().into()),
443 Value::String("b".to_string().into())
444 );
445 }
446
447 #[test]
448 fn test_value_inequality_different_types() {
449 assert_ne!(Value::Int(1), Value::Bool(true));
450 assert_ne!(Value::Null, Value::Bool(false));
451 assert_ne!(Value::String("42".to_string().into()), Value::Int(42));
452 }
453
454 #[test]
455 fn test_value_clone() {
456 let values = vec![
457 Value::Null,
458 Value::Bool(true),
459 Value::Int(42),
460 Value::Float(3.5),
461 Value::String("test".to_string().into()),
462 Value::Reference(Reference::local("id")),
463 ];
464
465 for v in values {
466 let cloned = v.clone();
467 assert_eq!(v, cloned);
468 }
469 }
470
471 #[test]
472 fn test_value_debug() {
473 let v = Value::Int(42);
474 let debug = format!("{:?}", v);
475 assert!(debug.contains("Int"));
476 assert!(debug.contains("42"));
477 }
478
479 #[test]
482 fn test_value_int_bounds() {
483 assert_eq!(Value::Int(i64::MAX).as_int(), Some(i64::MAX));
484 assert_eq!(Value::Int(i64::MIN).as_int(), Some(i64::MIN));
485 }
486
487 #[test]
488 fn test_value_empty_string() {
489 let v = Value::String(String::new().into());
490 assert_eq!(v.as_str(), Some(""));
491 }
492
493 #[test]
494 fn test_value_unicode_string() {
495 let v = Value::String("日本語 🎉".to_string().into());
496 assert_eq!(v.as_str(), Some("日本語 🎉"));
497 }
498
499 #[test]
500 fn test_value_float_special() {
501 let inf = Value::Float(f64::INFINITY);
502 assert!(inf.as_float().unwrap().is_infinite());
503
504 let nan = Value::Float(f64::NAN);
505 assert!(nan.as_float().unwrap().is_nan());
506 }
507
508 #[test]
509 fn test_reference_empty_id() {
510 let r = Reference::local("");
511 assert_eq!(r.id.as_ref(), "");
512 assert_eq!(r.to_ref_string(), "@");
513 }
514
515 #[test]
516 fn test_reference_with_special_chars() {
517 let r = Reference::local("id-with-hyphens-123");
518 assert_eq!(r.to_ref_string(), "@id-with-hyphens-123");
519 }
520}