1use alloc::{
4 string::{String, ToString},
5 sync::Arc,
6 vec::Vec,
7};
8use core::fmt;
9
10use crate::compat::HashMap;
11
12#[derive(Debug, Clone)]
14pub enum Value {
15 Str(String),
17 Bool(bool),
19 Int(i64),
21 Float(f64),
23 List(Arc<Vec<Value>>),
25 Struct(Arc<HashMap<String, Value>>),
27 Tmpl(Arc<crate::template::Template>),
29 None,
31}
32
33impl PartialEq for Value {
34 fn eq(&self, other: &Self) -> bool {
35 match (self, other) {
36 (Self::Str(a), Self::Str(b)) => a == b,
37 (Self::Bool(a), Self::Bool(b)) => a == b,
38 (Self::Int(a), Self::Int(b)) => a == b,
39 (Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
40 (Self::List(a), Self::List(b)) => a == b,
41 (Self::Struct(a), Self::Struct(b)) => a == b,
42 (Self::Tmpl(a), Self::Tmpl(b)) => Arc::ptr_eq(a, b),
43 (Self::None, Self::None) => true,
44 _ => false,
45 }
46 }
47}
48
49impl Eq for Value {}
50
51impl fmt::Display for Value {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Self::Str(s) => f.write_str(s),
55 Self::Bool(b) => write!(f, "{b}"),
56 Self::Int(i) => {
57 let mut buf = itoa::Buffer::new();
58 f.write_str(buf.format(*i))
59 }
60 Self::Float(v) => write!(f, "{v}"),
61 Self::List(items) => write!(f, "[<list of {}>]", items.len()),
62 Self::Struct(map) => write!(f, "{{<struct of {}>}}", map.len()),
63 Self::Tmpl(_) => write!(f, "<template>"),
64 Self::None => Ok(()),
65 }
66 }
67}
68
69impl Value {
70 #[must_use]
72 pub fn is_truthy(&self) -> bool {
73 match self {
74 Self::Str(s) => !s.is_empty(),
75 Self::Bool(b) => *b,
76 Self::Int(i) => *i != 0,
77 Self::Float(f) => *f != 0.0,
78 Self::List(v) => !v.is_empty(),
79 Self::Struct(m) => !m.is_empty(),
80 Self::Tmpl(_) => true,
81 Self::None => false,
82 }
83 }
84 #[must_use]
86 pub fn type_name(&self) -> &'static str {
87 match self {
88 Self::Str(_) => crate::consts::TYPE_STR,
89 Self::Bool(_) => crate::consts::TYPE_BOOL,
90 Self::Int(_) => crate::consts::TYPE_INT,
91 Self::Float(_) => crate::consts::TYPE_FLOAT,
92 Self::List(_) => crate::consts::TYPE_LIST,
93 Self::Struct(_) => crate::consts::TYPE_STRUCT,
94 Self::Tmpl(_) => crate::consts::TYPE_TMPL,
95 Self::None => "none",
96 }
97 }
98 #[inline]
103 #[must_use]
104 pub fn get_field(&self, key: &str) -> Option<&Value> {
105 match self {
106 Self::Struct(m) => {
107 if key == crate::consts::ENUM_TAG_KEY {
109 return None;
110 }
111 m.get(key)
112 }
113 _ => None,
114 }
115 }
116
117 #[must_use]
119 pub fn is_str(&self) -> bool {
120 matches!(self, Self::Str(_))
121 }
122
123 #[must_use]
125 pub fn is_int(&self) -> bool {
126 matches!(self, Self::Int(_))
127 }
128
129 #[must_use]
131 pub fn is_float(&self) -> bool {
132 matches!(self, Self::Float(_))
133 }
134
135 #[must_use]
137 pub fn is_bool(&self) -> bool {
138 matches!(self, Self::Bool(_))
139 }
140
141 #[must_use]
143 pub fn is_list(&self) -> bool {
144 matches!(self, Self::List(_))
145 }
146
147 #[must_use]
149 pub fn is_struct(&self) -> bool {
150 matches!(self, Self::Struct(_))
151 }
152
153 #[must_use]
155 pub fn as_str(&self) -> Option<&str> {
156 match self {
157 Self::Str(s) => Some(s),
158 _ => None,
159 }
160 }
161
162 #[must_use]
164 pub fn as_int(&self) -> Option<i64> {
165 match self {
166 Self::Int(i) => Some(*i),
167 _ => None,
168 }
169 }
170
171 #[must_use]
173 pub fn as_float(&self) -> Option<f64> {
174 match self {
175 Self::Float(f) => Some(*f),
176 _ => None,
177 }
178 }
179
180 #[must_use]
182 pub fn as_bool(&self) -> Option<bool> {
183 match self {
184 Self::Bool(b) => Some(*b),
185 _ => None,
186 }
187 }
188
189 #[must_use]
191 pub fn as_list(&self) -> Option<&[Value]> {
192 match self {
193 Self::List(v) => Some(v),
194 _ => None,
195 }
196 }
197
198 #[must_use]
200 pub fn as_struct(&self) -> Option<&HashMap<String, Value>> {
201 match self {
202 Self::Struct(m) => Some(m),
203 _ => None,
204 }
205 }
206
207 #[must_use]
209 pub fn as_tmpl(&self) -> Option<&Arc<crate::template::Template>> {
210 match self {
211 Self::Tmpl(t) => Some(t),
212 _ => None,
213 }
214 }
215
216 #[must_use]
229 pub fn new_struct<I, K, V>(pairs: I) -> Self
230 where
231 I: IntoIterator<Item = (K, V)>,
232 K: Into<String>,
233 V: Into<Value>,
234 {
235 Self::Struct(Arc::new(
236 pairs
237 .into_iter()
238 .map(|(k, v)| (k.into(), v.into()))
239 .collect(),
240 ))
241 }
242
243 #[must_use]
259 pub fn list<I, V>(items: I) -> Self
260 where
261 I: IntoIterator<Item = V>,
262 V: Into<Value>,
263 {
264 Self::List(Arc::new(items.into_iter().map(Into::into).collect()))
265 }
266}
267
268#[cfg(feature = "serde")]
269impl Value {
270 pub fn from_serialize<T: serde::Serialize>(
297 value: &T,
298 ) -> Result<Self, crate::serde_support::SerError> {
299 crate::serde_support::to_value(value)
300 }
301
302 pub fn deserialize_into<'de, T: serde::Deserialize<'de>>(
332 &'de self,
333 ) -> Result<T, crate::serde_support::DeError> {
334 crate::serde_support::from_value(self)
335 }
336}
337
338#[cfg(feature = "std")]
341#[cfg(feature = "serde")]
342impl Value {
343 pub fn from_flexbuffers(data: &[u8]) -> Result<Self, crate::error::TemplateError> {
349 let r = flexbuffers::Reader::get_root(data).map_err(|e| {
350 crate::error::TemplateError::syntax(format!("flexbuffers root error: {e}"))
351 })?;
352 serde::Deserialize::deserialize(r).map_err(|e| {
353 crate::error::TemplateError::syntax(format!("flexbuffers deserialization failed: {e}"))
354 })
355 }
356}
357
358impl From<&str> for Value {
363 fn from(s: &str) -> Self {
364 Self::Str(s.to_string())
365 }
366}
367
368impl From<String> for Value {
369 fn from(s: String) -> Self {
370 Self::Str(s)
371 }
372}
373
374impl From<bool> for Value {
375 fn from(b: bool) -> Self {
376 Self::Bool(b)
377 }
378}
379
380impl From<i64> for Value {
381 fn from(i: i64) -> Self {
382 Self::Int(i)
383 }
384}
385
386impl From<i32> for Value {
387 fn from(i: i32) -> Self {
388 Self::Int(i64::from(i))
389 }
390}
391
392impl From<u32> for Value {
393 fn from(i: u32) -> Self {
394 Self::Int(i64::from(i))
395 }
396}
397
398impl TryFrom<u64> for Value {
399 type Error = core::num::TryFromIntError;
400 fn try_from(i: u64) -> Result<Self, Self::Error> {
401 Ok(Self::Int(i64::try_from(i)?))
402 }
403}
404
405impl TryFrom<usize> for Value {
406 type Error = core::num::TryFromIntError;
407 fn try_from(i: usize) -> Result<Self, Self::Error> {
408 Ok(Self::Int(i64::try_from(i)?))
409 }
410}
411
412impl From<f64> for Value {
413 fn from(f: f64) -> Self {
414 Self::Float(f)
415 }
416}
417
418impl From<f32> for Value {
419 fn from(f: f32) -> Self {
420 Self::Float(f64::from(f))
421 }
422}
423
424impl From<Vec<Value>> for Value {
425 fn from(v: Vec<Value>) -> Self {
426 Self::List(Arc::new(v))
427 }
428}
429
430impl From<HashMap<String, Value>> for Value {
431 fn from(m: HashMap<String, Value>) -> Self {
432 Self::Struct(Arc::new(m))
433 }
434}
435
436impl From<crate::template::Template> for Value {
437 fn from(t: crate::template::Template) -> Self {
438 Self::Tmpl(Arc::new(t))
439 }
440}
441
442impl From<Arc<crate::template::Template>> for Value {
443 fn from(t: Arc<crate::template::Template>) -> Self {
444 Self::Tmpl(t)
445 }
446}
447
448impl From<&crate::template::Template> for Value {
449 fn from(t: &crate::template::Template) -> Self {
450 Self::Tmpl(Arc::new(t.clone()))
451 }
452}
453
454#[derive(Debug, Clone, PartialEq, Eq)]
460pub struct ValueTypeError {
461 pub expected: &'static str,
463 pub actual: &'static str,
465}
466
467impl fmt::Display for ValueTypeError {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 write!(f, "expected {}, got {}", self.expected, self.actual)
470 }
471}
472
473impl core::error::Error for ValueTypeError {}
474
475impl TryFrom<Value> for String {
476 type Error = ValueTypeError;
477 fn try_from(v: Value) -> Result<Self, Self::Error> {
478 match v {
479 Value::Str(s) => Ok(s),
480 other => Err(ValueTypeError {
481 expected: crate::consts::TYPE_STR,
482 actual: other.type_name(),
483 }),
484 }
485 }
486}
487
488impl TryFrom<Value> for i64 {
489 type Error = ValueTypeError;
490 fn try_from(v: Value) -> Result<Self, Self::Error> {
491 match v {
492 Value::Int(i) => Ok(i),
493 other => Err(ValueTypeError {
494 expected: crate::consts::TYPE_INT,
495 actual: other.type_name(),
496 }),
497 }
498 }
499}
500
501impl TryFrom<Value> for f64 {
502 type Error = ValueTypeError;
503 fn try_from(v: Value) -> Result<Self, Self::Error> {
504 match v {
505 Value::Float(f) => Ok(f),
506 other => Err(ValueTypeError {
507 expected: crate::consts::TYPE_FLOAT,
508 actual: other.type_name(),
509 }),
510 }
511 }
512}
513
514impl TryFrom<Value> for bool {
515 type Error = ValueTypeError;
516 fn try_from(v: Value) -> Result<Self, Self::Error> {
517 match v {
518 Value::Bool(b) => Ok(b),
519 other => Err(ValueTypeError {
520 expected: crate::consts::TYPE_BOOL,
521 actual: other.type_name(),
522 }),
523 }
524 }
525}
526
527impl TryFrom<Value> for Vec<Value> {
528 type Error = ValueTypeError;
529 fn try_from(v: Value) -> Result<Self, Self::Error> {
530 match v {
531 Value::List(l) => Ok(Arc::try_unwrap(l).unwrap_or_else(|arc| (*arc).clone())),
532 other => Err(ValueTypeError {
533 expected: crate::consts::TYPE_LIST,
534 actual: other.type_name(),
535 }),
536 }
537 }
538}
539
540impl<S: core::hash::BuildHasher + Default> TryFrom<Value> for HashMap<String, Value, S> {
541 type Error = ValueTypeError;
542 fn try_from(v: Value) -> Result<Self, Self::Error> {
543 match v {
544 Value::Struct(m) => {
545 let owned = Arc::try_unwrap(m).unwrap_or_else(|arc| (*arc).clone());
546 Ok(owned.into_iter().collect())
547 }
548 other => Err(ValueTypeError {
549 expected: crate::consts::TYPE_STRUCT,
550 actual: other.type_name(),
551 }),
552 }
553 }
554}
555
556#[cfg(test)]
561mod tests {
562 use super::*;
563
564 #[test]
567 fn display_str() {
568 assert_eq!(Value::Str("hello".into()).to_string(), "hello");
569 }
570
571 #[test]
572 fn display_bool() {
573 assert_eq!(Value::Bool(true).to_string(), "true");
574 assert_eq!(Value::Bool(false).to_string(), "false");
575 }
576
577 #[test]
578 fn display_int() {
579 assert_eq!(Value::Int(42).to_string(), "42");
580 assert_eq!(Value::Int(-7).to_string(), "-7");
581 }
582
583 #[test]
584 fn display_float() {
585 assert_eq!(Value::Float(3.25).to_string(), "3.25");
586 }
587
588 #[test]
589 fn display_list() {
590 let list = Value::List(Arc::new(vec![Value::Int(1)]));
591 assert_eq!(list.to_string(), "[<list of 1>]");
592 assert_eq!(Value::List(Arc::new(vec![])).to_string(), "[<list of 0>]");
593 }
594
595 #[test]
596 fn display_dict() {
597 let dict = Value::Struct(Arc::new(HashMap::from([("k".into(), Value::Int(1))])));
598 assert_eq!(dict.to_string(), "{<struct of 1>}");
599 assert_eq!(
600 Value::Struct(Arc::new(HashMap::new())).to_string(),
601 "{<struct of 0>}"
602 );
603 }
604
605 #[test]
608 fn truthy_str() {
609 assert!(Value::Str("hello".into()).is_truthy());
610 assert!(!Value::Str(String::new()).is_truthy());
611 }
612
613 #[test]
614 fn truthy_bool() {
615 assert!(Value::Bool(true).is_truthy());
616 assert!(!Value::Bool(false).is_truthy());
617 }
618
619 #[test]
620 fn truthy_int() {
621 assert!(Value::Int(1).is_truthy());
622 assert!(Value::Int(-1).is_truthy());
623 assert!(!Value::Int(0).is_truthy());
624 }
625
626 #[test]
627 fn truthy_float() {
628 assert!(Value::Float(0.1).is_truthy());
629 assert!(!Value::Float(0.0).is_truthy());
630 }
631
632 #[test]
633 fn truthy_list() {
634 assert!(Value::List(Arc::new(vec![Value::Int(1)])).is_truthy());
635 assert!(!Value::List(Arc::new(vec![])).is_truthy());
636 }
637
638 #[test]
639 fn truthy_dict() {
640 let populated = Value::Struct(Arc::new(HashMap::from([("k".into(), Value::Int(1))])));
641 assert!(populated.is_truthy());
642 assert!(!Value::Struct(Arc::new(HashMap::new())).is_truthy());
643 }
644
645 #[test]
648 fn type_names() {
649 assert_eq!(Value::Str("x".into()).type_name(), "str");
650 assert_eq!(Value::Bool(true).type_name(), "bool");
651 assert_eq!(Value::Int(0).type_name(), "int");
652 assert_eq!(Value::Float(0.0).type_name(), "float");
653 assert_eq!(Value::List(Arc::new(vec![])).type_name(), "list");
654 assert_eq!(
655 Value::Struct(Arc::new(HashMap::new())).type_name(),
656 "struct"
657 );
658 }
659
660 #[test]
663 fn get_field_on_dict() {
664 let dict = Value::Struct(Arc::new(HashMap::from([
665 ("name".into(), Value::Str("Alice".into())),
666 ("score".into(), Value::Int(95)),
667 ])));
668 assert_eq!(dict.get_field("name"), Some(&Value::Str("Alice".into())));
669 assert_eq!(dict.get_field("score"), Some(&Value::Int(95)));
670 assert_eq!(dict.get_field("missing"), None);
671 }
672
673 #[test]
674 fn get_field_on_non_dict_returns_none() {
675 assert_eq!(Value::Str("x".into()).get_field("any"), None);
676 assert_eq!(Value::Int(1).get_field("any"), None);
677 assert_eq!(Value::List(Arc::new(vec![])).get_field("any"), None);
678 }
679
680 #[test]
683 fn from_str_ref() {
684 let v: Value = "hello".into();
685 assert_eq!(v, Value::Str("hello".into()));
686 }
687
688 #[test]
689 fn from_string() {
690 let v: Value = String::from("world").into();
691 assert_eq!(v, Value::Str("world".into()));
692 }
693
694 #[test]
695 fn from_bool() {
696 let v: Value = true.into();
697 assert_eq!(v, Value::Bool(true));
698 }
699
700 #[test]
701 fn from_i64() {
702 let v: Value = 42_i64.into();
703 assert_eq!(v, Value::Int(42));
704 }
705
706 #[test]
707 fn from_i32() {
708 let v: Value = 7_i32.into();
709 assert_eq!(v, Value::Int(7));
710 }
711
712 #[test]
713 fn from_u32() {
714 let v: Value = 100_u32.into();
715 assert_eq!(v, Value::Int(100));
716 }
717
718 #[test]
719 fn try_from_u64() {
720 let v = Value::try_from(999_u64).unwrap();
721 assert_eq!(v, Value::Int(999));
722 }
723
724 #[test]
725 fn try_from_u64_overflow() {
726 let result = Value::try_from(u64::MAX);
727 assert!(result.is_err(), "u64::MAX should not fit in i64");
728 }
729
730 #[test]
731 fn try_from_usize() {
732 let v = Value::try_from(5_usize).unwrap();
733 assert_eq!(v, Value::Int(5));
734 }
735
736 #[test]
737 fn from_f64() {
738 let v: Value = 2.5_f64.into();
739 assert_eq!(v, Value::Float(2.5));
740 }
741
742 #[test]
743 fn from_f32() {
744 let v: Value = 1.5_f32.into();
745 assert!(matches!(v, Value::Float(f) if (f - 1.5).abs() < f64::EPSILON));
747 }
748
749 #[test]
750 fn from_vec_value() {
751 let items = vec![Value::Int(1), Value::Str("two".into())];
752 let v: Value = items.into();
753 assert!(matches!(v, Value::List(ref l) if l.len() == 2));
754 }
755
756 #[test]
757 fn from_hashmap_value() {
758 let map = HashMap::from([("k".into(), Value::Bool(true))]);
759 let v: Value = map.into();
760 assert_eq!(v.get_field("k"), Some(&Value::Bool(true)));
761 }
762
763 #[test]
766 fn as_str_returns_some_for_str() {
767 assert_eq!(Value::Str("hello".into()).as_str(), Some("hello"));
768 }
769
770 #[test]
771 fn as_str_returns_none_for_non_str() {
772 assert_eq!(Value::Int(42).as_str(), None);
773 }
774
775 #[test]
776 fn as_int_returns_some_for_int() {
777 assert_eq!(Value::Int(42).as_int(), Some(42));
778 }
779
780 #[test]
781 fn as_int_returns_none_for_non_int() {
782 assert_eq!(Value::Str("42".into()).as_int(), None);
783 }
784
785 #[test]
786 fn as_float_returns_some_for_float() {
787 assert_eq!(Value::Float(3.25).as_float(), Some(3.25));
788 }
789
790 #[test]
791 fn as_float_returns_none_for_non_float() {
792 assert_eq!(Value::Int(3).as_float(), None);
793 }
794
795 #[test]
796 fn as_bool_returns_some_for_bool() {
797 assert_eq!(Value::Bool(true).as_bool(), Some(true));
798 }
799
800 #[test]
801 fn as_bool_returns_none_for_non_bool() {
802 assert_eq!(Value::Str("true".into()).as_bool(), None);
803 }
804
805 #[test]
806 fn as_list_returns_some_for_list() {
807 let items = vec![Value::Int(1), Value::Int(2)];
808 let v = Value::List(Arc::new(items.clone()));
809 assert_eq!(v.as_list(), Some(items.as_slice()));
810 }
811
812 #[test]
813 fn as_list_returns_none_for_non_list() {
814 assert_eq!(Value::Int(1).as_list(), None);
815 }
816
817 #[test]
818 fn as_struct_returns_some_for_dict() {
819 let map = HashMap::from([("k".into(), Value::Int(1))]);
820 let v = Value::Struct(Arc::new(map.clone()));
821 assert_eq!(v.as_struct(), Some(&map));
822 }
823
824 #[test]
825 fn as_struct_returns_none_for_non_dict() {
826 assert_eq!(Value::Int(1).as_struct(), None);
827 }
828
829 #[test]
832 fn try_from_str_success() {
833 let v = Value::Str("hello".into());
834 assert_eq!(String::try_from(v).unwrap(), "hello");
835 }
836
837 #[test]
838 fn try_from_str_failure_has_message() {
839 let v = Value::Int(42);
840 let err = String::try_from(v).unwrap_err();
841 assert_eq!(err.expected, "str");
842 assert_eq!(err.actual, "int");
843 assert_eq!(err.to_string(), "expected str, got int");
844 }
845
846 #[test]
847 fn try_from_i64_success() {
848 let v = Value::Int(99);
849 assert_eq!(i64::try_from(v).unwrap(), 99);
850 }
851
852 #[test]
853 fn try_from_i64_failure() {
854 let v = Value::Str("99".into());
855 let err = i64::try_from(v).expect_err("Str should not convert to i64");
856 assert_eq!(err.expected, "int");
857 assert_eq!(err.actual, "str");
858 }
859
860 #[test]
861 fn try_from_f64_success() {
862 let v = Value::Float(2.5);
863 assert!((f64::try_from(v).unwrap() - 2.5).abs() < f64::EPSILON);
864 }
865
866 #[test]
867 fn try_from_bool_success() {
868 let v = Value::Bool(false);
869 assert!(!bool::try_from(v).unwrap());
870 }
871
872 #[test]
873 fn try_from_vec_success() {
874 let v = Value::List(Arc::new(vec![Value::Int(1)]));
875 let list = Vec::<Value>::try_from(v).unwrap();
876 assert_eq!(list.len(), 1);
877 }
878
879 #[test]
880 fn try_from_hashmap_success() {
881 let v = Value::Struct(Arc::new(HashMap::from([("k".into(), Value::Int(1))])));
882 let map = HashMap::<String, Value>::try_from(v).unwrap();
883 assert_eq!(map.len(), 1);
884 }
885
886 #[test]
887 fn from_template_owned() {
888 let tmpl = crate::Template::from_source(
889 r"---
890params: [x = str]
891---
892{{ x }}",
893 )
894 .unwrap();
895 let val = Value::from(tmpl);
896 assert!(matches!(val, Value::Tmpl(_)));
897 assert_eq!(val.type_name(), "tmpl");
898 }
899
900 #[test]
901 fn from_template_ref() {
902 let tmpl = crate::Template::from_source(
903 r"---
904params: [x = str]
905---
906{{ x }}",
907 )
908 .unwrap();
909 let val = Value::from(&tmpl);
910 assert!(matches!(val, Value::Tmpl(_)));
911 }
912
913 #[test]
914 fn from_template_arc() {
915 let tmpl = crate::Template::from_source(
916 r"---
917params: [x = str]
918---
919{{ x }}",
920 )
921 .unwrap();
922 let arc = Arc::new(tmpl);
923 let val = Value::from(arc);
924 assert!(matches!(val, Value::Tmpl(_)));
925 }
926
927 #[test]
928 fn context_set_with_template() {
929 let tmpl = crate::Template::from_source(
930 r"---
931params: [x = str]
932---
933{{ x }}",
934 )
935 .unwrap();
936 let mut ctx = crate::Context::new();
937 ctx.set("widget", tmpl);
939 assert!(ctx.get("widget").unwrap().as_tmpl().is_some());
940 }
941
942 #[test]
943 fn context_set_with_template_ref() {
944 let tmpl = crate::Template::from_source(
945 r"---
946params: [x = str]
947---
948{{ x }}",
949 )
950 .unwrap();
951 let mut ctx = crate::Context::new();
952 ctx.set("widget", &tmpl);
954 assert!(ctx.get("widget").unwrap().as_tmpl().is_some());
955 }
956}