cedar_policy_core/ast/
value.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::ast::*;
18use crate::parser::Loc;
19use std::collections::{BTreeMap, BTreeSet, HashSet};
20use std::str::FromStr;
21use std::sync::Arc;
22
23use educe::Educe;
24use itertools::Itertools;
25use miette::Diagnostic;
26use smol_str::SmolStr;
27use thiserror::Error;
28
29/// This describes all the values which could be the dynamic result of evaluating an `Expr`.
30/// Cloning is O(1).
31#[derive(Educe, Debug, Clone)]
32#[educe(PartialEq, Eq, PartialOrd, Ord)]
33pub struct Value {
34    /// Underlying actual value
35    pub value: ValueKind,
36    /// Source location associated with the value, if any
37    #[educe(PartialEq(ignore))]
38    #[educe(PartialOrd(ignore))]
39    pub loc: Option<Loc>,
40}
41
42/// This describes all the values which could be the dynamic result of evaluating an `Expr`.
43/// Cloning is O(1).
44#[derive(Debug, Clone, PartialOrd, Ord)]
45pub enum ValueKind {
46    /// anything that is a Literal can also be the dynamic result of evaluating an `Expr`
47    Lit(Literal),
48    /// Evaluating an `Expr` can result in a first-class set
49    Set(Set),
50    /// Evaluating an `Expr` can result in a first-class anonymous record (keyed on String)
51    Record(Arc<BTreeMap<SmolStr, Value>>),
52    /// Evaluating an `Expr` can result in an extension value
53    ExtensionValue(Arc<RepresentableExtensionValue>),
54}
55
56impl Value {
57    /// Create a new empty set
58    pub fn empty_set(loc: Option<Loc>) -> Self {
59        Self {
60            value: ValueKind::empty_set(),
61            loc,
62        }
63    }
64
65    /// Create a new empty record
66    pub fn empty_record(loc: Option<Loc>) -> Self {
67        Self {
68            value: ValueKind::empty_record(),
69            loc,
70        }
71    }
72
73    /// Create a `Value` from anything that implements `Into<ValueKind>` and an
74    /// optional source location
75    pub fn new(value: impl Into<ValueKind>, loc: Option<Loc>) -> Self {
76        Self {
77            value: value.into(),
78            loc,
79        }
80    }
81
82    /// Create a set with the given `Value`s as elements
83    pub fn set(vals: impl IntoIterator<Item = Value>, loc: Option<Loc>) -> Self {
84        Self {
85            value: ValueKind::set(vals),
86            loc,
87        }
88    }
89
90    /// Create a set with the given `Literal`s as elements
91    ///
92    /// the resulting `Value` will have the given `loc` attached, but its
93    /// individual `Literal` elements will not have a source loc attached
94    pub fn set_of_lits(lits: impl IntoIterator<Item = Literal>, loc: Option<Loc>) -> Self {
95        Self {
96            value: ValueKind::set_of_lits(lits),
97            loc,
98        }
99    }
100
101    /// Create a record with the given (key, value) pairs
102    pub fn record<K: Into<SmolStr>, V: Into<Value>>(
103        pairs: impl IntoIterator<Item = (K, V)>,
104        loc: Option<Loc>,
105    ) -> Self {
106        Self {
107            value: ValueKind::record(pairs),
108            loc,
109        }
110    }
111
112    /// Create a record with the given attributes/value mapping.
113    pub fn record_arc(pairs: Arc<BTreeMap<SmolStr, Value>>, loc: Option<Loc>) -> Self {
114        Self {
115            value: ValueKind::record_arc(pairs),
116            loc,
117        }
118    }
119
120    /// Return the `Value`, but with the given `Loc` (or `None`)
121    pub fn with_maybe_source_loc(self, loc: Option<Loc>) -> Self {
122        Self { loc, ..self }
123    }
124
125    /// Get the `ValueKind` for this `Value`
126    pub fn value_kind(&self) -> &ValueKind {
127        &self.value
128    }
129
130    /// Get the `Loc` attached to this `Value`, if there is one
131    pub fn source_loc(&self) -> Option<&Loc> {
132        self.loc.as_ref()
133    }
134
135    /// If the value is a `Literal`, get a reference to the underlying `Literal`
136    pub(crate) fn try_as_lit(&self) -> Option<&Literal> {
137        self.value.try_as_lit()
138    }
139
140    /// The `PartialEq` and `Eq` implementations for `Value` ignore the source location.
141    /// If you actually want to check that two values are equal _and_ have the
142    /// same source location, you can use this.
143    pub fn eq_and_same_source_loc(&self, other: &Self) -> bool {
144        self == other && self.source_loc() == other.source_loc()
145    }
146}
147
148impl BoundedDisplay for Value {
149    fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
150        BoundedDisplay::fmt(&self.value, f, n)
151    }
152}
153
154impl ValueKind {
155    /// Create a new empty set
156    pub fn empty_set() -> Self {
157        Self::Set(Set::empty())
158    }
159
160    /// Create a new empty record
161    pub fn empty_record() -> Self {
162        Self::Record(Arc::new(BTreeMap::new()))
163    }
164
165    /// Create a set with the given `Value`s as elements
166    pub fn set(vals: impl IntoIterator<Item = Value>) -> Self {
167        Self::Set(Set::new(vals))
168    }
169
170    /// Create a set with the given `Literal`s as elements
171    pub fn set_of_lits(lits: impl IntoIterator<Item = Literal>) -> Self {
172        Self::Set(Set::from_lits(lits))
173    }
174
175    /// Create a record with the given (key, value) pairs
176    pub fn record<K: Into<SmolStr>, V: Into<Value>>(
177        pairs: impl IntoIterator<Item = (K, V)>,
178    ) -> Self {
179        Self::Record(Arc::new(
180            pairs
181                .into_iter()
182                .map(|(k, v)| (k.into(), v.into()))
183                .collect(),
184        ))
185    }
186
187    /// Create a record with the given attributes/value mapping.
188    pub fn record_arc(pairs: Arc<BTreeMap<SmolStr, Value>>) -> Self {
189        Self::Record(pairs)
190    }
191
192    /// If the value is a `Literal`, get a reference to the underlying `Literal`
193    pub(crate) fn try_as_lit(&self) -> Option<&Literal> {
194        match &self {
195            Self::Lit(lit) => Some(lit),
196            _ => None,
197        }
198    }
199}
200
201impl BoundedDisplay for ValueKind {
202    fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
203        match self {
204            Self::Lit(lit) => write!(f, "{lit}"),
205            Self::Set(Set {
206                fast,
207                authoritative,
208            }) => {
209                write!(f, "[")?;
210                let truncated = n.map(|n| authoritative.len() > n).unwrap_or(false);
211                if let Some(rc) = fast {
212                    // sort the elements, because we want the Display output to be
213                    // deterministic, particularly for tests which check equality
214                    // of error messages
215                    let elements = match n {
216                        Some(n) => Box::new(rc.as_ref().iter().sorted_unstable().take(n))
217                            as Box<dyn Iterator<Item = &Literal>>,
218                        None => Box::new(rc.as_ref().iter().sorted_unstable())
219                            as Box<dyn Iterator<Item = &Literal>>,
220                    };
221                    for (i, item) in elements.enumerate() {
222                        write!(f, "{item}")?;
223                        if i < authoritative.len() - 1 {
224                            write!(f, ", ")?;
225                        }
226                    }
227                } else {
228                    // don't need to sort the elements in this case because BTreeSet iterates
229                    // in a deterministic order already
230                    let elements = match n {
231                        Some(n) => Box::new(authoritative.as_ref().iter().take(n))
232                            as Box<dyn Iterator<Item = &Value>>,
233                        None => Box::new(authoritative.as_ref().iter())
234                            as Box<dyn Iterator<Item = &Value>>,
235                    };
236                    for (i, item) in elements.enumerate() {
237                        BoundedDisplay::fmt(item, f, n)?;
238                        if i < authoritative.len() - 1 {
239                            write!(f, ", ")?;
240                        }
241                    }
242                }
243                if truncated {
244                    write!(f, ".. ")?;
245                }
246                write!(f, "]")?;
247                Ok(())
248            }
249            Self::Record(record) => {
250                write!(f, "{{")?;
251                let truncated = n.map(|n| record.len() > n).unwrap_or(false);
252                // no need to sort the elements because BTreeMap iterates in a
253                // deterministic order already
254                let elements = match n {
255                    Some(n) => Box::new(record.as_ref().iter().take(n))
256                        as Box<dyn Iterator<Item = (&SmolStr, &Value)>>,
257                    None => Box::new(record.as_ref().iter())
258                        as Box<dyn Iterator<Item = (&SmolStr, &Value)>>,
259                };
260                for (i, (k, v)) in elements.enumerate() {
261                    match UnreservedId::from_str(k) {
262                        Ok(k) => {
263                            // we can omit the quotes around the key, it's a valid identifier and not a reserved keyword
264                            write!(f, "{k}: ")?;
265                        }
266                        Err(_) => {
267                            // put quotes around the key
268                            write!(f, "\"{k}\": ")?;
269                        }
270                    }
271                    BoundedDisplay::fmt(v, f, n)?;
272                    if i < record.len() - 1 {
273                        write!(f, ", ")?;
274                    }
275                }
276                if truncated {
277                    write!(f, ".. ")?;
278                }
279                write!(f, "}}")?;
280                Ok(())
281            }
282            Self::ExtensionValue(ev) => write!(f, "{}", RestrictedExpr::from(ev.as_ref().clone())),
283        }
284    }
285}
286
287#[derive(Debug, Error)]
288/// An error that can be thrown converting an expression to a value
289pub enum NotValue {
290    /// General error for non-values
291    #[error("not a value")]
292    NotValue {
293        /// Source location info for the expr that wasn't a value
294        loc: Option<Loc>,
295    },
296}
297
298impl Diagnostic for NotValue {
299    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
300        match self {
301            Self::NotValue { loc } => loc.as_ref().map(|loc| {
302                Box::new(std::iter::once(miette::LabeledSpan::underline(loc.span))) as _
303            }),
304        }
305    }
306
307    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
308        match self {
309            Self::NotValue { loc } => loc.as_ref().map(|loc| &loc.src as &dyn miette::SourceCode),
310        }
311    }
312}
313
314impl TryFrom<Expr> for Value {
315    type Error = NotValue;
316
317    fn try_from(expr: Expr) -> Result<Self, Self::Error> {
318        let loc = expr.source_loc().cloned();
319        Ok(Self {
320            value: ValueKind::try_from(expr)?,
321            loc,
322        })
323    }
324}
325
326impl TryFrom<Expr> for ValueKind {
327    type Error = NotValue;
328
329    fn try_from(expr: Expr) -> Result<Self, Self::Error> {
330        let loc = expr.source_loc().cloned();
331        match expr.into_expr_kind() {
332            ExprKind::Lit(lit) => Ok(Self::Lit(lit)),
333            ExprKind::Unknown(_) => Err(NotValue::NotValue { loc }),
334            ExprKind::Var(_) => Err(NotValue::NotValue { loc }),
335            ExprKind::Slot(_) => Err(NotValue::NotValue { loc }),
336            ExprKind::If { .. } => Err(NotValue::NotValue { loc }),
337            ExprKind::And { .. } => Err(NotValue::NotValue { loc }),
338            ExprKind::Or { .. } => Err(NotValue::NotValue { loc }),
339            ExprKind::UnaryApp { .. } => Err(NotValue::NotValue { loc }),
340            ExprKind::BinaryApp { .. } => Err(NotValue::NotValue { loc }),
341            ExprKind::ExtensionFunctionApp { .. } => Err(NotValue::NotValue { loc }),
342            ExprKind::GetAttr { .. } => Err(NotValue::NotValue { loc }),
343            ExprKind::HasAttr { .. } => Err(NotValue::NotValue { loc }),
344            ExprKind::Like { .. } => Err(NotValue::NotValue { loc }),
345            ExprKind::Is { .. } => Err(NotValue::NotValue { loc }),
346            ExprKind::Set(members) => members
347                .iter()
348                .map(|e| Value::try_from(e.clone()))
349                .collect::<Result<Set, _>>()
350                .map(Self::Set),
351            ExprKind::Record(map) => map
352                .iter()
353                .map(|(k, v)| Value::try_from(v.clone()).map(|v| (k.clone(), v)))
354                .collect::<Result<BTreeMap<SmolStr, Value>, _>>()
355                .map(|m| Self::Record(Arc::new(m))),
356            #[cfg(feature = "tolerant-ast")]
357            ExprKind::Error { .. } => Err(NotValue::NotValue { loc }),
358        }
359    }
360}
361
362/// `Value`'s internal representation of a `Set`
363#[derive(Debug, Clone)]
364pub struct Set {
365    /// the values in the set, stored in a `BTreeSet`
366    pub authoritative: Arc<BTreeSet<Value>>,
367    /// if possible, `HashSet<Literal>` representation of the set.
368    /// (This is possible if all the elements are literals.)
369    /// Some operations are much faster in this case.
370    ///
371    /// INVARIANT (FastRepr)
372    /// we guarantee that if the elements are all
373    /// literals, then this will be `Some`. (This allows us to further
374    /// optimize e.g. equality checks between sets: for instance, we know
375    /// that if one set has `fast` and another does not, the sets can't be
376    /// equal.)
377    pub fast: Option<Arc<HashSet<Literal>>>,
378}
379
380impl Set {
381    /// Create an empty set
382    pub fn empty() -> Self {
383        Self {
384            authoritative: Arc::new(BTreeSet::new()),
385            fast: Some(Arc::new(HashSet::new())),
386        }
387    }
388
389    /// Create a set with the given `Value`s as elements
390    pub fn new(vals: impl IntoIterator<Item = Value>) -> Self {
391        let authoritative: BTreeSet<Value> = vals.into_iter().collect();
392        let fast: Option<Arc<HashSet<Literal>>> = authoritative
393            .iter()
394            .map(|v| v.try_as_lit().cloned())
395            .collect::<Option<HashSet<Literal>>>()
396            .map(Arc::new);
397        Self {
398            authoritative: Arc::new(authoritative),
399            fast,
400        }
401    }
402
403    /// Create a set with the given `Literal`s as elements
404    pub fn from_lits(lits: impl IntoIterator<Item = Literal>) -> Self {
405        let fast: HashSet<Literal> = lits.into_iter().collect();
406        let authoritative: BTreeSet<Value> = fast
407            .iter()
408            .map(|lit| Value {
409                value: ValueKind::Lit(lit.clone()),
410                loc: None,
411            })
412            .collect();
413        Self {
414            authoritative: Arc::new(authoritative),
415            fast: Some(Arc::new(fast)),
416        }
417    }
418
419    /// Get the number of items in the set
420    pub fn len(&self) -> usize {
421        self.authoritative.len()
422    }
423
424    /// Convenience method to check if a set is empty
425    pub fn is_empty(&self) -> bool {
426        self.len() == 0
427    }
428
429    /// Borrowed iterator
430    pub fn iter(&self) -> impl Iterator<Item = &Value> {
431        self.authoritative.iter()
432    }
433}
434
435impl FromIterator<Value> for Set {
436    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
437        let (literals, non_literals): (BTreeSet<_>, BTreeSet<_>) = iter
438            .into_iter()
439            .partition(|v| matches!(&v.value, ValueKind::Lit { .. }));
440
441        if non_literals.is_empty() {
442            Self::from_iter(literals.into_iter().map(|v| match v {
443                Value {
444                    value: ValueKind::Lit(lit),
445                    ..
446                } => lit,
447                // PANIC SAFETY: This is unreachable as every item in `literals` matches ValueKind::Lit
448                #[allow(clippy::unreachable)]
449                _ => unreachable!(),
450            }))
451        } else {
452            // INVARIANT (FastRepr)
453            // There are non-literals, so we need `fast` should be `None`
454            // We also need to add all the literals back into the set
455            let mut all_items = non_literals;
456            let mut literals = literals;
457            all_items.append(&mut literals);
458            Self {
459                authoritative: Arc::new(all_items),
460                fast: None,
461            }
462        }
463    }
464}
465
466impl FromIterator<Literal> for Set {
467    fn from_iter<T: IntoIterator<Item = Literal>>(iter: T) -> Self {
468        // INVARIANT (FastRepr)
469        // There are 0 non-literals, so we need to populate `fast`
470        let fast: HashSet<Literal> = iter.into_iter().collect();
471        Self {
472            authoritative: Arc::new(fast.iter().cloned().map(Into::into).collect()),
473            fast: Some(Arc::new(fast)),
474        }
475    }
476}
477
478// Trying to derive `PartialEq` for `ValueKind` fails with a compile error (at
479// least, as of this writing), so we write out the implementation manually.
480impl PartialEq for ValueKind {
481    fn eq(&self, other: &Self) -> bool {
482        match (self, other) {
483            (ValueKind::Lit(lit1), ValueKind::Lit(lit2)) => lit1 == lit2,
484            (ValueKind::Set(set1), ValueKind::Set(set2)) => set1 == set2,
485            (ValueKind::Record(r1), ValueKind::Record(r2)) => r1 == r2,
486            (ValueKind::ExtensionValue(ev1), ValueKind::ExtensionValue(ev2)) => ev1 == ev2,
487            (_, _) => false, // values of different types are not equal
488        }
489    }
490}
491
492impl Eq for ValueKind {}
493
494// PartialEq on Set is optimized to take advantage of the internal invariant documented on `Set`
495impl PartialEq for Set {
496    fn eq(&self, other: &Self) -> bool {
497        match (self.fast.as_ref(), other.fast.as_ref()) {
498            (Some(rc1), Some(rc2)) => rc1 == rc2,
499            (Some(_), None) => false, // due to internal invariant documented on `Set`, we know that one set contains a non-literal and the other does not
500            (None, Some(_)) => false, // due to internal invariant documented on `Set`, we know that one set contains a non-literal and the other does not
501            (None, None) => self.authoritative.as_ref() == other.authoritative.as_ref(),
502        }
503    }
504}
505impl Eq for Set {}
506
507// Ord on Set compares only the `authoritative` version; note that HashSet
508// doesn't implement Ord
509impl Ord for Set {
510    fn cmp(&self, other: &Set) -> std::cmp::Ordering {
511        self.authoritative
512            .as_ref()
513            .cmp(other.authoritative.as_ref())
514    }
515}
516
517impl PartialOrd<Set> for Set {
518    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
519        // delegate to `Ord`
520        Some(self.cmp(other))
521    }
522}
523
524impl StaticallyTyped for Value {
525    fn type_of(&self) -> Type {
526        self.value.type_of()
527    }
528}
529
530impl StaticallyTyped for ValueKind {
531    fn type_of(&self) -> Type {
532        match self {
533            Self::Lit(lit) => lit.type_of(),
534            Self::Set(_) => Type::Set,
535            Self::Record(_) => Type::Record,
536            Self::ExtensionValue(ev) => ev.type_of(),
537        }
538    }
539}
540
541/// Like `Display`, but optionally truncates embedded sets/records to `n`
542/// elements/pairs, including recursively.
543///
544/// `n`: the maximum number of set elements, or record key-value pairs, that
545/// will be shown before eliding the rest with `..`.
546/// `None` means no bound.
547///
548/// Intended for error messages, to avoid very large/long error messages.
549pub trait BoundedDisplay {
550    /// Write `self` to the writer `f`, truncating set elements or key-value
551    /// pairs if necessary based on `n`
552    fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result;
553
554    /// Convenience method, equivalent to `fmt()` with `n` as `Some`.
555    ///
556    /// You should generally not re-implement this, just use the default implementation.
557    fn fmt_bounded(&self, f: &mut impl std::fmt::Write, n: usize) -> std::fmt::Result {
558        self.fmt(f, Some(n))
559    }
560
561    /// Convenience method, equivalent to `fmt()` with `n` as `None`.
562    ///
563    /// You should generally not re-implement this, just use the default implementation.
564    fn fmt_unbounded(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
565        self.fmt(f, None)
566    }
567}
568
569/// Like `ToString`, but optionally truncates embedded sets/records to `n`
570/// elements/pairs, including recursively.
571///
572/// `n`: the maximum number of set elements, or record key-value pairs, that
573/// will be shown before eliding the rest with `..`.
574/// `None` means no bound.
575///
576/// Intended for error messages, to avoid very large/long error messages.
577pub trait BoundedToString {
578    /// Convert `self` to a `String`, truncating set elements or key-value pairs
579    /// if necessary based on `n`
580    fn to_string(&self, n: Option<usize>) -> String;
581
582    /// Convenience method, equivalent to `to_string()` with `n` as `Some`.
583    ///
584    /// You should generally not re-implement this, just use the default implementation.
585    fn to_string_bounded(&self, n: usize) -> String {
586        self.to_string(Some(n))
587    }
588
589    /// Convenience method, equivalent to `to_string()` with `n` as `None`.
590    ///
591    /// You should generally not re-implement this, just use the default implementation.
592    fn to_string_unbounded(&self) -> String {
593        self.to_string(None)
594    }
595}
596
597/// Like the impl of `ToString` for `T: Display` in the standard library,
598/// this impl of `BoundedToString` for `T: BoundedDisplay` panics if the `BoundedDisplay`
599/// implementation returns an error, which would indicate an incorrect `BoundedDisplay`
600/// implementation since `fmt::Write`-ing to a `String` never returns an error.
601impl<T: BoundedDisplay> BoundedToString for T {
602    fn to_string(&self, n: Option<usize>) -> String {
603        let mut s = String::new();
604        // PANIC SAFETY: `std::fmt::Write` does not return errors when writing to a `String`
605        #[allow(clippy::expect_used)]
606        BoundedDisplay::fmt(self, &mut s, n).expect("a `BoundedDisplay` implementation returned an error when writing to a `String`, which shouldn't happen");
607        s
608    }
609}
610
611impl std::fmt::Display for Value {
612    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
613        write!(f, "{}", self.value)
614    }
615}
616
617impl std::fmt::Display for ValueKind {
618    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
619        BoundedDisplay::fmt_unbounded(self, f)
620    }
621}
622
623/// Create a `Value` directly from a `Vec<Value>`, or `Vec<T> where T: Into<Value>`
624/// (so `Vec<Integer>`, `Vec<String>`, etc)
625///
626/// This impl does not propagate source location; the resulting `Value` will
627/// have no source location info attached
628impl<T: Into<Value>> From<Vec<T>> for Value {
629    fn from(v: Vec<T>) -> Self {
630        Self::set(v.into_iter().map(Into::into), None)
631    }
632}
633
634/// Create a `ValueKind` directly from a `Vec<Value>`, or `Vec<T> where T: Into<Value>`
635/// (so `Vec<Integer>`, `Vec<String>`, etc)
636impl<T: Into<Value>> From<Vec<T>> for ValueKind {
637    fn from(v: Vec<T>) -> Self {
638        Self::set(v.into_iter().map(Into::into))
639    }
640}
641
642/// Create a `Value` directly from a `Literal`, or from anything that implements
643/// `Into<Literal>` (so `Integer`, `&str`, `EntityUID`, etc)
644///
645/// This impl does not propagate source location; the resulting `Value` will
646/// have no source location info attached
647impl<T: Into<Literal>> From<T> for Value {
648    fn from(lit: T) -> Self {
649        Self {
650            value: lit.into().into(),
651            loc: None,
652        }
653    }
654}
655
656/// Create a `ValueKind` directly from a `Literal`, or from anything that implements
657/// `Into<Literal>` (so `Integer`, `&str`, `EntityUID`, etc)
658impl<T: Into<Literal>> From<T> for ValueKind {
659    fn from(lit: T) -> Self {
660        Self::Lit(lit.into())
661    }
662}
663
664// PANIC SAFETY: Unit Test Code
665#[allow(clippy::panic)]
666#[cfg(test)]
667mod test {
668    use super::*;
669
670    #[test]
671    fn values() {
672        assert_eq!(
673            Value::from(true),
674            Value {
675                value: ValueKind::Lit(Literal::Bool(true)),
676                loc: None,
677            },
678        );
679        assert_eq!(
680            Value::from(false),
681            Value {
682                value: ValueKind::Lit(Literal::Bool(false)),
683                loc: None,
684            },
685        );
686        assert_eq!(
687            Value::from(23),
688            Value {
689                value: ValueKind::Lit(Literal::Long(23)),
690                loc: None,
691            },
692        );
693        assert_eq!(
694            Value::from(-47),
695            Value {
696                value: ValueKind::Lit(Literal::Long(-47)),
697                loc: None,
698            },
699        );
700        assert_eq!(
701            Value::from("hello"),
702            Value {
703                value: ValueKind::Lit(Literal::String("hello".into())),
704                loc: None,
705            },
706        );
707        assert_eq!(
708            Value::from("hello".to_owned()),
709            Value {
710                value: ValueKind::Lit(Literal::String("hello".into())),
711                loc: None,
712            },
713        );
714        assert_eq!(
715            Value::from(String::new()),
716            Value {
717                value: ValueKind::Lit(Literal::String(SmolStr::default())),
718                loc: None,
719            },
720        );
721        assert_eq!(
722            Value::from(""),
723            Value {
724                value: ValueKind::Lit(Literal::String(SmolStr::default())),
725                loc: None,
726            },
727        );
728        assert_eq!(
729            Value::from(vec![2, -3, 40]),
730            Value::set(vec![Value::from(2), Value::from(-3), Value::from(40)], None),
731        );
732        assert_eq!(
733            Value::from(vec![Literal::from(false), Literal::from("eggs")]),
734            Value::set(vec![Value::from(false), Value::from("eggs")], None),
735        );
736        assert_eq!(
737            Value::set(vec![Value::from(false), Value::from("eggs")], None),
738            Value::set_of_lits(vec![Literal::from(false), Literal::from("eggs")], None),
739        );
740
741        let mut rec1: BTreeMap<SmolStr, Value> = BTreeMap::new();
742        rec1.insert("ham".into(), 3.into());
743        rec1.insert("eggs".into(), "hickory".into());
744        assert_eq!(
745            Value::record(rec1.clone(), None),
746            Value {
747                value: ValueKind::Record(Arc::new(rec1)),
748                loc: None,
749            },
750        );
751
752        let mut rec2: BTreeMap<SmolStr, Value> = BTreeMap::new();
753        rec2.insert("hi".into(), "ham".into());
754        rec2.insert("eggs".into(), "hickory".into());
755        assert_eq!(
756            Value::record(vec![("hi", "ham"), ("eggs", "hickory"),], None),
757            Value {
758                value: ValueKind::Record(Arc::new(rec2)),
759                loc: None,
760            },
761        );
762
763        assert_eq!(
764            Value::from(EntityUID::with_eid("foo")),
765            Value {
766                value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid("foo")))),
767                loc: None,
768            },
769        );
770    }
771
772    #[test]
773    fn value_types() {
774        assert_eq!(Value::from(false).type_of(), Type::Bool);
775        assert_eq!(Value::from(23).type_of(), Type::Long);
776        assert_eq!(Value::from(-47).type_of(), Type::Long);
777        assert_eq!(Value::from("hello").type_of(), Type::String);
778        assert_eq!(Value::from(vec![2, -3, 40]).type_of(), Type::Set);
779        assert_eq!(Value::empty_set(None).type_of(), Type::Set);
780        assert_eq!(Value::empty_record(None).type_of(), Type::Record);
781        assert_eq!(
782            Value::record(vec![("hello", Value::from("ham"))], None).type_of(),
783            Type::Record
784        );
785        assert_eq!(
786            Value::from(EntityUID::with_eid("foo")).type_of(),
787            Type::entity_type(
788                Name::parse_unqualified_name("test_entity_type").expect("valid identifier")
789            )
790        );
791    }
792
793    #[test]
794    fn test_set_is_empty_for_empty_set() {
795        let set = Set {
796            authoritative: Arc::new(BTreeSet::new()),
797            fast: Some(Arc::new(HashSet::new())),
798        };
799        assert!(set.is_empty());
800    }
801
802    #[test]
803    fn test_set_is_not_empty_for_set_with_values() {
804        let set = Set {
805            authoritative: Arc::new(BTreeSet::from([Value::from("abc")])),
806            fast: None,
807        };
808        assert!(!set.is_empty());
809    }
810
811    #[test]
812    fn pretty_printer() {
813        assert_eq!(ToString::to_string(&Value::from("abc")), r#""abc""#);
814        assert_eq!(ToString::to_string(&Value::from("\t")), r#""\t""#);
815        assert_eq!(ToString::to_string(&Value::from("🐈")), r#""🐈""#);
816    }
817
818    #[test]
819    fn set_collect() {
820        let v = vec![Value {
821            value: 1.into(),
822            loc: None,
823        }];
824        let set: Set = v.into_iter().collect();
825        assert_eq!(set.len(), 1);
826        let v2 = vec![Value {
827            value: ValueKind::Set(set),
828            loc: None,
829        }];
830        let set2: Set = v2.into_iter().collect();
831        assert_eq!(set2.len(), 1);
832    }
833}