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    /// All literal uids referenced by this value
148    pub fn all_literal_uids(&self) -> HashSet<EntityUID> {
149        self.value.all_literal_uids()
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    /// All literal uids referenced by this value kind
206    pub fn all_literal_uids(&self) -> HashSet<EntityUID> {
207        match self {
208            ValueKind::Lit(Literal::EntityUID(uid)) => HashSet::from([uid.as_ref().clone()]),
209            ValueKind::Lit(_) => HashSet::new(),
210            ValueKind::Set(set) => set.iter().flat_map(Value::all_literal_uids).collect(),
211            ValueKind::Record(record) => {
212                record.values().flat_map(Value::all_literal_uids).collect()
213            }
214            ValueKind::ExtensionValue(_) => HashSet::new(),
215        }
216    }
217}
218
219impl BoundedDisplay for ValueKind {
220    fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
221        match self {
222            Self::Lit(lit) => write!(f, "{lit}"),
223            Self::Set(Set {
224                fast,
225                authoritative,
226            }) => {
227                write!(f, "[")?;
228                let truncated = n.map(|n| authoritative.len() > n).unwrap_or(false);
229                if let Some(rc) = fast {
230                    // sort the elements, because we want the Display output to be
231                    // deterministic, particularly for tests which check equality
232                    // of error messages
233                    let elements = match n {
234                        Some(n) => Box::new(rc.as_ref().iter().sorted_unstable().take(n))
235                            as Box<dyn Iterator<Item = &Literal>>,
236                        None => Box::new(rc.as_ref().iter().sorted_unstable())
237                            as Box<dyn Iterator<Item = &Literal>>,
238                    };
239                    for (i, item) in elements.enumerate() {
240                        write!(f, "{item}")?;
241                        if i < authoritative.len() - 1 {
242                            write!(f, ", ")?;
243                        }
244                    }
245                } else {
246                    // don't need to sort the elements in this case because BTreeSet iterates
247                    // in a deterministic order already
248                    let elements = match n {
249                        Some(n) => Box::new(authoritative.as_ref().iter().take(n))
250                            as Box<dyn Iterator<Item = &Value>>,
251                        None => Box::new(authoritative.as_ref().iter())
252                            as Box<dyn Iterator<Item = &Value>>,
253                    };
254                    for (i, item) in elements.enumerate() {
255                        BoundedDisplay::fmt(item, f, n)?;
256                        if i < authoritative.len() - 1 {
257                            write!(f, ", ")?;
258                        }
259                    }
260                }
261                if truncated {
262                    write!(f, ".. ")?;
263                }
264                write!(f, "]")?;
265                Ok(())
266            }
267            Self::Record(record) => {
268                write!(f, "{{")?;
269                let truncated = n.map(|n| record.len() > n).unwrap_or(false);
270                // no need to sort the elements because BTreeMap iterates in a
271                // deterministic order already
272                let elements = match n {
273                    Some(n) => Box::new(record.as_ref().iter().take(n))
274                        as Box<dyn Iterator<Item = (&SmolStr, &Value)>>,
275                    None => Box::new(record.as_ref().iter())
276                        as Box<dyn Iterator<Item = (&SmolStr, &Value)>>,
277                };
278                for (i, (k, v)) in elements.enumerate() {
279                    match UnreservedId::from_str(k) {
280                        Ok(k) => {
281                            // we can omit the quotes around the key, it's a valid identifier and not a reserved keyword
282                            write!(f, "{k}: ")?;
283                        }
284                        Err(_) => {
285                            // put quotes around the key
286                            write!(f, "\"{k}\": ")?;
287                        }
288                    }
289                    BoundedDisplay::fmt(v, f, n)?;
290                    if i < record.len() - 1 {
291                        write!(f, ", ")?;
292                    }
293                }
294                if truncated {
295                    write!(f, ".. ")?;
296                }
297                write!(f, "}}")?;
298                Ok(())
299            }
300            Self::ExtensionValue(ev) => write!(f, "{}", RestrictedExpr::from(ev.as_ref().clone())),
301        }
302    }
303}
304
305#[derive(Debug, Error)]
306/// An error that can be thrown converting an expression to a value
307pub enum NotValue {
308    /// General error for non-values
309    #[error("not a value")]
310    NotValue {
311        /// Source location info for the expr that wasn't a value
312        loc: Option<Loc>,
313    },
314}
315
316impl Diagnostic for NotValue {
317    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
318        match self {
319            Self::NotValue { loc } => loc.as_ref().map(|loc| {
320                Box::new(std::iter::once(miette::LabeledSpan::underline(loc.span))) as _
321            }),
322        }
323    }
324
325    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
326        match self {
327            Self::NotValue { loc } => loc.as_ref().map(|loc| &loc.src as &dyn miette::SourceCode),
328        }
329    }
330}
331
332impl TryFrom<Expr> for Value {
333    type Error = NotValue;
334
335    fn try_from(expr: Expr) -> Result<Self, Self::Error> {
336        let loc = expr.source_loc().cloned();
337        Ok(Self {
338            value: ValueKind::try_from(expr)?,
339            loc,
340        })
341    }
342}
343
344impl TryFrom<Expr> for ValueKind {
345    type Error = NotValue;
346
347    fn try_from(expr: Expr) -> Result<Self, Self::Error> {
348        let loc = expr.source_loc().cloned();
349        match expr.into_expr_kind() {
350            ExprKind::Lit(lit) => Ok(Self::Lit(lit)),
351            ExprKind::Unknown(_) => Err(NotValue::NotValue { loc }),
352            ExprKind::Var(_) => Err(NotValue::NotValue { loc }),
353            ExprKind::Slot(_) => Err(NotValue::NotValue { loc }),
354            ExprKind::If { .. } => Err(NotValue::NotValue { loc }),
355            ExprKind::And { .. } => Err(NotValue::NotValue { loc }),
356            ExprKind::Or { .. } => Err(NotValue::NotValue { loc }),
357            ExprKind::UnaryApp { .. } => Err(NotValue::NotValue { loc }),
358            ExprKind::BinaryApp { .. } => Err(NotValue::NotValue { loc }),
359            ExprKind::ExtensionFunctionApp { .. } => Err(NotValue::NotValue { loc }),
360            ExprKind::GetAttr { .. } => Err(NotValue::NotValue { loc }),
361            ExprKind::HasAttr { .. } => Err(NotValue::NotValue { loc }),
362            ExprKind::Like { .. } => Err(NotValue::NotValue { loc }),
363            ExprKind::Is { .. } => Err(NotValue::NotValue { loc }),
364            ExprKind::Set(members) => members
365                .iter()
366                .map(|e| Value::try_from(e.clone()))
367                .collect::<Result<Set, _>>()
368                .map(Self::Set),
369            ExprKind::Record(map) => map
370                .iter()
371                .map(|(k, v)| Value::try_from(v.clone()).map(|v| (k.clone(), v)))
372                .collect::<Result<BTreeMap<SmolStr, Value>, _>>()
373                .map(|m| Self::Record(Arc::new(m))),
374            #[cfg(feature = "tolerant-ast")]
375            ExprKind::Error { .. } => Err(NotValue::NotValue { loc }),
376        }
377    }
378}
379
380/// `Value`'s internal representation of a `Set`
381#[derive(Debug, Clone)]
382pub struct Set {
383    /// the values in the set, stored in a `BTreeSet`
384    pub authoritative: Arc<BTreeSet<Value>>,
385    /// if possible, `HashSet<Literal>` representation of the set.
386    /// (This is possible if all the elements are literals.)
387    /// Some operations are much faster in this case.
388    ///
389    /// INVARIANT (FastRepr)
390    /// we guarantee that if the elements are all
391    /// literals, then this will be `Some`. (This allows us to further
392    /// optimize e.g. equality checks between sets: for instance, we know
393    /// that if one set has `fast` and another does not, the sets can't be
394    /// equal.)
395    pub fast: Option<Arc<HashSet<Literal>>>,
396}
397
398impl Set {
399    /// Create an empty set
400    pub fn empty() -> Self {
401        Self {
402            authoritative: Arc::new(BTreeSet::new()),
403            fast: Some(Arc::new(HashSet::new())),
404        }
405    }
406
407    /// Create a set with the given `Value`s as elements
408    pub fn new(vals: impl IntoIterator<Item = Value>) -> Self {
409        let authoritative: BTreeSet<Value> = vals.into_iter().collect();
410        let fast: Option<Arc<HashSet<Literal>>> = authoritative
411            .iter()
412            .map(|v| v.try_as_lit().cloned())
413            .collect::<Option<HashSet<Literal>>>()
414            .map(Arc::new);
415        Self {
416            authoritative: Arc::new(authoritative),
417            fast,
418        }
419    }
420
421    /// Create a set with the given `Literal`s as elements
422    pub fn from_lits(lits: impl IntoIterator<Item = Literal>) -> Self {
423        let fast: HashSet<Literal> = lits.into_iter().collect();
424        let authoritative: BTreeSet<Value> = fast
425            .iter()
426            .map(|lit| Value {
427                value: ValueKind::Lit(lit.clone()),
428                loc: None,
429            })
430            .collect();
431        Self {
432            authoritative: Arc::new(authoritative),
433            fast: Some(Arc::new(fast)),
434        }
435    }
436
437    /// Get the number of items in the set
438    pub fn len(&self) -> usize {
439        self.authoritative.len()
440    }
441
442    /// Convenience method to check if a set is empty
443    pub fn is_empty(&self) -> bool {
444        self.len() == 0
445    }
446
447    /// Borrowed iterator
448    pub fn iter(&self) -> impl Iterator<Item = &Value> {
449        self.authoritative.iter()
450    }
451
452    /// Subset test
453    pub fn is_subset(&self, other: &Set) -> bool {
454        match (&self.fast, &other.fast) {
455            // both sets are in fast form, ie, they only contain literals.
456            // Fast hashset-based implementation.
457            (Some(ls1), Some(ls2)) => ls1.is_subset(ls2.as_ref()),
458            // `self` contains non-literal(s), `other` is all-literal.
459            // The invariant about `Set::fast` should allow us to conclude that
460            // the result is `false`
461            (None, Some(_)) => false,
462            // one or both sets are in slow form, ie, contain a non-literal.
463            // Fallback to slow implementation.
464            _ => self.authoritative.is_subset(&other.authoritative),
465        }
466    }
467
468    /// Disjointness test
469    pub fn is_disjoint(&self, other: &Set) -> bool {
470        match (&self.fast, &other.fast) {
471            // both sets are in fast form, ie, they only contain literals.
472            // Fast hashset-based implementation.
473            (Some(ls1), Some(ls2)) => ls1.is_disjoint(ls2.as_ref()),
474            // one or both sets are in slow form, ie, contain a non-literal.
475            // Fallback to slow implementation.
476            _ => self.authoritative.is_disjoint(&other.authoritative),
477        }
478    }
479
480    /// Membership test
481    pub fn contains(&self, value: &Value) -> bool {
482        match (&self.fast, &value.value) {
483            // both sets are in fast form, ie, they only contain literals.
484            // Fast hashset-based implementation.
485            (Some(ls), ValueKind::Lit(lit)) => ls.contains(lit),
486            // Set is all-literal but `value` is not a literal
487            // The invariant about `Set::fast` should allow us to conclude that
488            // the result is `false`
489            (Some(_), _) => false,
490            // Set contains a non-literal
491            // Fallback to slow implementation.
492            _ => self.authoritative.contains(value),
493        }
494    }
495}
496
497impl FromIterator<Value> for Set {
498    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
499        let (literals, non_literals): (BTreeSet<_>, BTreeSet<_>) = iter
500            .into_iter()
501            .partition(|v| matches!(&v.value, ValueKind::Lit { .. }));
502
503        if non_literals.is_empty() {
504            Self::from_iter(literals.into_iter().map(|v| match v {
505                Value {
506                    value: ValueKind::Lit(lit),
507                    ..
508                } => lit,
509                // PANIC SAFETY: This is unreachable as every item in `literals` matches ValueKind::Lit
510                #[allow(clippy::unreachable)]
511                _ => unreachable!(),
512            }))
513        } else {
514            // INVARIANT (FastRepr)
515            // There are non-literals, so we need `fast` should be `None`
516            // We also need to add all the literals back into the set
517            let mut all_items = non_literals;
518            let mut literals = literals;
519            all_items.append(&mut literals);
520            Self {
521                authoritative: Arc::new(all_items),
522                fast: None,
523            }
524        }
525    }
526}
527
528impl FromIterator<Literal> for Set {
529    fn from_iter<T: IntoIterator<Item = Literal>>(iter: T) -> Self {
530        // INVARIANT (FastRepr)
531        // There are 0 non-literals, so we need to populate `fast`
532        let fast: HashSet<Literal> = iter.into_iter().collect();
533        Self {
534            authoritative: Arc::new(fast.iter().cloned().map(Into::into).collect()),
535            fast: Some(Arc::new(fast)),
536        }
537    }
538}
539
540// Trying to derive `PartialEq` for `ValueKind` fails with a compile error (at
541// least, as of this writing), so we write out the implementation manually.
542impl PartialEq for ValueKind {
543    fn eq(&self, other: &Self) -> bool {
544        match (self, other) {
545            (ValueKind::Lit(lit1), ValueKind::Lit(lit2)) => lit1 == lit2,
546            (ValueKind::Set(set1), ValueKind::Set(set2)) => set1 == set2,
547            (ValueKind::Record(r1), ValueKind::Record(r2)) => r1 == r2,
548            (ValueKind::ExtensionValue(ev1), ValueKind::ExtensionValue(ev2)) => ev1 == ev2,
549            (_, _) => false, // values of different types are not equal
550        }
551    }
552}
553
554impl Eq for ValueKind {}
555
556// PartialEq on Set is optimized to take advantage of the internal invariant documented on `Set`
557impl PartialEq for Set {
558    fn eq(&self, other: &Self) -> bool {
559        match (self.fast.as_ref(), other.fast.as_ref()) {
560            (Some(rc1), Some(rc2)) => rc1 == rc2,
561            (Some(_), None) => false, // due to internal invariant documented on `Set`, we know that one set contains a non-literal and the other does not
562            (None, Some(_)) => false, // due to internal invariant documented on `Set`, we know that one set contains a non-literal and the other does not
563            (None, None) => self.authoritative.as_ref() == other.authoritative.as_ref(),
564        }
565    }
566}
567impl Eq for Set {}
568
569// Ord on Set compares only the `authoritative` version; note that HashSet
570// doesn't implement Ord
571impl Ord for Set {
572    fn cmp(&self, other: &Set) -> std::cmp::Ordering {
573        self.authoritative
574            .as_ref()
575            .cmp(other.authoritative.as_ref())
576    }
577}
578
579impl PartialOrd<Set> for Set {
580    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
581        // delegate to `Ord`
582        Some(self.cmp(other))
583    }
584}
585
586impl StaticallyTyped for Value {
587    fn type_of(&self) -> Type {
588        self.value.type_of()
589    }
590}
591
592impl StaticallyTyped for ValueKind {
593    fn type_of(&self) -> Type {
594        match self {
595            Self::Lit(lit) => lit.type_of(),
596            Self::Set(_) => Type::Set,
597            Self::Record(_) => Type::Record,
598            Self::ExtensionValue(ev) => ev.type_of(),
599        }
600    }
601}
602
603/// Like `Display`, but optionally truncates embedded sets/records to `n`
604/// elements/pairs, including recursively.
605///
606/// `n`: the maximum number of set elements, or record key-value pairs, that
607/// will be shown before eliding the rest with `..`.
608/// `None` means no bound.
609///
610/// Intended for error messages, to avoid very large/long error messages.
611pub trait BoundedDisplay {
612    /// Write `self` to the writer `f`, truncating set elements or key-value
613    /// pairs if necessary based on `n`
614    fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result;
615
616    /// Convenience method, equivalent to `fmt()` with `n` as `Some`.
617    ///
618    /// You should generally not re-implement this, just use the default implementation.
619    fn fmt_bounded(&self, f: &mut impl std::fmt::Write, n: usize) -> std::fmt::Result {
620        self.fmt(f, Some(n))
621    }
622
623    /// Convenience method, equivalent to `fmt()` with `n` as `None`.
624    ///
625    /// You should generally not re-implement this, just use the default implementation.
626    fn fmt_unbounded(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
627        self.fmt(f, None)
628    }
629}
630
631/// Like `ToString`, but optionally truncates embedded sets/records to `n`
632/// elements/pairs, including recursively.
633///
634/// `n`: the maximum number of set elements, or record key-value pairs, that
635/// will be shown before eliding the rest with `..`.
636/// `None` means no bound.
637///
638/// Intended for error messages, to avoid very large/long error messages.
639pub trait BoundedToString {
640    /// Convert `self` to a `String`, truncating set elements or key-value pairs
641    /// if necessary based on `n`
642    fn to_string(&self, n: Option<usize>) -> String;
643
644    /// Convenience method, equivalent to `to_string()` with `n` as `Some`.
645    ///
646    /// You should generally not re-implement this, just use the default implementation.
647    fn to_string_bounded(&self, n: usize) -> String {
648        self.to_string(Some(n))
649    }
650
651    /// Convenience method, equivalent to `to_string()` with `n` as `None`.
652    ///
653    /// You should generally not re-implement this, just use the default implementation.
654    fn to_string_unbounded(&self) -> String {
655        self.to_string(None)
656    }
657}
658
659/// Like the impl of `ToString` for `T: Display` in the standard library,
660/// this impl of `BoundedToString` for `T: BoundedDisplay` panics if the `BoundedDisplay`
661/// implementation returns an error, which would indicate an incorrect `BoundedDisplay`
662/// implementation since `fmt::Write`-ing to a `String` never returns an error.
663impl<T: BoundedDisplay> BoundedToString for T {
664    fn to_string(&self, n: Option<usize>) -> String {
665        let mut s = String::new();
666        // PANIC SAFETY: `std::fmt::Write` does not return errors when writing to a `String`
667        #[allow(clippy::expect_used)]
668        BoundedDisplay::fmt(self, &mut s, n).expect("a `BoundedDisplay` implementation returned an error when writing to a `String`, which shouldn't happen");
669        s
670    }
671}
672
673impl std::fmt::Display for Value {
674    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
675        write!(f, "{}", self.value)
676    }
677}
678
679impl std::fmt::Display for ValueKind {
680    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
681        BoundedDisplay::fmt_unbounded(self, f)
682    }
683}
684
685/// Create a `Value` directly from a `Vec<Value>`, or `Vec<T> where T: Into<Value>`
686/// (so `Vec<Integer>`, `Vec<String>`, etc)
687///
688/// This impl does not propagate source location; the resulting `Value` will
689/// have no source location info attached
690impl<T: Into<Value>> From<Vec<T>> for Value {
691    fn from(v: Vec<T>) -> Self {
692        Self::set(v.into_iter().map(Into::into), None)
693    }
694}
695
696/// Create a `ValueKind` directly from a `Vec<Value>`, or `Vec<T> where T: Into<Value>`
697/// (so `Vec<Integer>`, `Vec<String>`, etc)
698impl<T: Into<Value>> From<Vec<T>> for ValueKind {
699    fn from(v: Vec<T>) -> Self {
700        Self::set(v.into_iter().map(Into::into))
701    }
702}
703
704/// Create a `Value` directly from a `Literal`, or from anything that implements
705/// `Into<Literal>` (so `Integer`, `&str`, `EntityUID`, etc)
706///
707/// This impl does not propagate source location; the resulting `Value` will
708/// have no source location info attached
709impl<T: Into<Literal>> From<T> for Value {
710    fn from(lit: T) -> Self {
711        Self {
712            value: lit.into().into(),
713            loc: None,
714        }
715    }
716}
717
718/// Create a `ValueKind` directly from a `Literal`, or from anything that implements
719/// `Into<Literal>` (so `Integer`, `&str`, `EntityUID`, etc)
720impl<T: Into<Literal>> From<T> for ValueKind {
721    fn from(lit: T) -> Self {
722        Self::Lit(lit.into())
723    }
724}
725
726// PANIC SAFETY: Unit Test Code
727#[allow(clippy::panic)]
728#[cfg(test)]
729mod test {
730    use super::*;
731
732    #[test]
733    fn values() {
734        assert_eq!(
735            Value::from(true),
736            Value {
737                value: ValueKind::Lit(Literal::Bool(true)),
738                loc: None,
739            },
740        );
741        assert_eq!(
742            Value::from(false),
743            Value {
744                value: ValueKind::Lit(Literal::Bool(false)),
745                loc: None,
746            },
747        );
748        assert_eq!(
749            Value::from(23),
750            Value {
751                value: ValueKind::Lit(Literal::Long(23)),
752                loc: None,
753            },
754        );
755        assert_eq!(
756            Value::from(-47),
757            Value {
758                value: ValueKind::Lit(Literal::Long(-47)),
759                loc: None,
760            },
761        );
762        assert_eq!(
763            Value::from("hello"),
764            Value {
765                value: ValueKind::Lit(Literal::String("hello".into())),
766                loc: None,
767            },
768        );
769        assert_eq!(
770            Value::from("hello".to_owned()),
771            Value {
772                value: ValueKind::Lit(Literal::String("hello".into())),
773                loc: None,
774            },
775        );
776        assert_eq!(
777            Value::from(String::new()),
778            Value {
779                value: ValueKind::Lit(Literal::String(SmolStr::default())),
780                loc: None,
781            },
782        );
783        assert_eq!(
784            Value::from(""),
785            Value {
786                value: ValueKind::Lit(Literal::String(SmolStr::default())),
787                loc: None,
788            },
789        );
790        assert_eq!(
791            Value::from(vec![2, -3, 40]),
792            Value::set(vec![Value::from(2), Value::from(-3), Value::from(40)], None),
793        );
794        assert_eq!(
795            Value::from(vec![Literal::from(false), Literal::from("eggs")]),
796            Value::set(vec![Value::from(false), Value::from("eggs")], None),
797        );
798        assert_eq!(
799            Value::set(vec![Value::from(false), Value::from("eggs")], None),
800            Value::set_of_lits(vec![Literal::from(false), Literal::from("eggs")], None),
801        );
802
803        let mut rec1: BTreeMap<SmolStr, Value> = BTreeMap::new();
804        rec1.insert("ham".into(), 3.into());
805        rec1.insert("eggs".into(), "hickory".into());
806        assert_eq!(
807            Value::record(rec1.clone(), None),
808            Value {
809                value: ValueKind::Record(Arc::new(rec1)),
810                loc: None,
811            },
812        );
813
814        let mut rec2: BTreeMap<SmolStr, Value> = BTreeMap::new();
815        rec2.insert("hi".into(), "ham".into());
816        rec2.insert("eggs".into(), "hickory".into());
817        assert_eq!(
818            Value::record(vec![("hi", "ham"), ("eggs", "hickory"),], None),
819            Value {
820                value: ValueKind::Record(Arc::new(rec2)),
821                loc: None,
822            },
823        );
824
825        assert_eq!(
826            Value::from(EntityUID::with_eid("foo")),
827            Value {
828                value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid("foo")))),
829                loc: None,
830            },
831        );
832    }
833
834    #[test]
835    fn value_types() {
836        assert_eq!(Value::from(false).type_of(), Type::Bool);
837        assert_eq!(Value::from(23).type_of(), Type::Long);
838        assert_eq!(Value::from(-47).type_of(), Type::Long);
839        assert_eq!(Value::from("hello").type_of(), Type::String);
840        assert_eq!(Value::from(vec![2, -3, 40]).type_of(), Type::Set);
841        assert_eq!(Value::empty_set(None).type_of(), Type::Set);
842        assert_eq!(Value::empty_record(None).type_of(), Type::Record);
843        assert_eq!(
844            Value::record(vec![("hello", Value::from("ham"))], None).type_of(),
845            Type::Record
846        );
847        assert_eq!(
848            Value::from(EntityUID::with_eid("foo")).type_of(),
849            Type::entity_type(
850                Name::parse_unqualified_name("test_entity_type").expect("valid identifier")
851            )
852        );
853    }
854
855    #[test]
856    fn test_set_is_empty_for_empty_set() {
857        let set = Set {
858            authoritative: Arc::new(BTreeSet::new()),
859            fast: Some(Arc::new(HashSet::new())),
860        };
861        assert!(set.is_empty());
862    }
863
864    #[test]
865    fn test_set_is_not_empty_for_set_with_values() {
866        let set = Set {
867            authoritative: Arc::new(BTreeSet::from([Value::from("abc")])),
868            fast: None,
869        };
870        assert!(!set.is_empty());
871    }
872
873    #[test]
874    fn pretty_printer() {
875        assert_eq!(ToString::to_string(&Value::from("abc")), r#""abc""#);
876        assert_eq!(ToString::to_string(&Value::from("\t")), r#""\t""#);
877        assert_eq!(ToString::to_string(&Value::from("🐈")), r#""🐈""#);
878    }
879
880    #[test]
881    fn set_collect() {
882        let v = vec![Value {
883            value: 1.into(),
884            loc: None,
885        }];
886        let set: Set = v.into_iter().collect();
887        assert_eq!(set.len(), 1);
888        let v2 = vec![Value {
889            value: ValueKind::Set(set),
890            loc: None,
891        }];
892        let set2: Set = v2.into_iter().collect();
893        assert_eq!(set2.len(), 1);
894    }
895}