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