Skip to main content

core_traits/
lib.rs

1// Copyright 2018 Mozilla
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4// this file except in compliance with the License. You may obtain a copy of the
5// License at http://www.apache.org/licenses/LICENSE-2.0
6// Unless required by applicable law or agreed to in writing, software distributed
7// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
8// CONDITIONS OF ANY KIND, either express or implied. See the License for the
9// specific language governing permissions and limitations under the License.
10
11extern crate chrono;
12extern crate enumset;
13extern crate indexmap;
14extern crate ordered_float;
15#[macro_use]
16extern crate serde_derive;
17extern crate bytes;
18extern crate edn;
19extern crate uuid;
20#[macro_use]
21extern crate lazy_static;
22
23use std::fmt;
24
25use std::ffi::CString;
26
27use std::ops::Deref;
28
29use std::os::raw::c_char;
30
31use std::rc::Rc;
32
33use std::sync::Arc;
34
35use std::collections::BTreeMap;
36
37use bytes::Bytes;
38use indexmap::IndexMap;
39
40use enumset::{EnumSet, EnumSetType};
41
42use ordered_float::OrderedFloat;
43
44use chrono::{DateTime, Timelike};
45
46use uuid::Uuid;
47
48use edn::{Cloned, FromMicros, FromRc, Keyword, Utc, ValueRc};
49
50use edn::entities::{
51    AttributePlace, EntidOrIdent, EntityPlace, TransactableValueMarker, ValuePlace,
52};
53
54mod value_type_set;
55pub mod values;
56
57pub use crate::value_type_set::ValueTypeSet;
58
59#[macro_export]
60macro_rules! bail {
61    ($e:expr) => {
62        return Err($e.into());
63    };
64}
65
66/// Represents one entid in the entid space.
67///
68/// Per https://www.sqlite.org/datatype3.html (see also http://stackoverflow.com/a/8499544), SQLite
69/// stores signed integers up to 64 bits in size.  Since u32 is not appropriate for our use case, we
70/// use i64 rather than manually truncating u64 to u63 and casting to i64 throughout the codebase.
71pub type Entid = i64;
72
73/// An entid that's either already in the store, or newly allocated to a tempid.
74/// TODO: we'd like to link this in some way to the lifetime of a particular PartitionMap.
75#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
76pub struct KnownEntid(pub Entid);
77
78impl From<KnownEntid> for Entid {
79    fn from(k: KnownEntid) -> Entid {
80        k.0
81    }
82}
83
84impl<V: TransactableValueMarker> From<KnownEntid> for EntityPlace<V> {
85    fn from(value: KnownEntid) -> Self {
86        Self::Entid(EntidOrIdent::Entid(value.0))
87    }
88}
89
90impl From<KnownEntid> for AttributePlace {
91    fn from(value: KnownEntid) -> Self {
92        Self::Entid(EntidOrIdent::Entid(value.0))
93    }
94}
95
96impl<V: TransactableValueMarker> From<KnownEntid> for ValuePlace<V> {
97    fn from(value: KnownEntid) -> Self {
98        Self::Entid(EntidOrIdent::Entid(value.0))
99    }
100}
101
102/// Bit flags used in `flags0` column in temporary tables created during search,
103/// such as the `search_results`, `inexact_searches` and `exact_searches` tables.
104/// When moving to a more concrete table, such as `datoms`, they are expanded out
105/// via these flags and put into their own column rather than a bit field.
106pub enum AttributeBitFlags {
107    IndexAVET = 1,
108    IndexVAET = 1 << 1,
109    IndexFulltext = 1 << 2,
110    UniqueValue = 1 << 3,
111}
112
113pub mod attribute {
114    use crate::TypedValue;
115
116    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
117    pub enum Unique {
118        Value,
119        Identity,
120    }
121
122    impl Unique {
123        // This is easier than rejigging DB_UNIQUE_VALUE to not be EDN.
124        pub fn into_typed_value(self) -> TypedValue {
125            match self {
126                Unique::Value => TypedValue::typed_ns_keyword("db.unique", "value"),
127                Unique::Identity => TypedValue::typed_ns_keyword("db.unique", "identity"),
128            }
129        }
130    }
131}
132
133/// A Mentat schema attribute has a value type and several other flags determining how assertions
134/// with the attribute are interpreted.
135///
136/// TODO: consider packing this into a bitfield or similar.
137#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
138pub struct Attribute {
139    /// The associated value type, i.e., `:db/valueType`?
140    pub value_type: ValueType,
141
142    /// `true` if this attribute is multi-valued, i.e., it is `:db/cardinality
143    /// :db.cardinality/many`.  `false` if this attribute is single-valued (the default), i.e., it
144    /// is `:db/cardinality :db.cardinality/one`.
145    pub multival: bool,
146
147    /// `None` if this attribute is neither unique-value nor unique-identity.
148    ///
149    /// `Some(attribute::Unique::Value)` if this attribute is unique-value, i.e., it is `:db/unique
150    /// :db.unique/value`.
151    ///
152    /// *Unique-value* means that there is at most one assertion with the attribute and a
153    /// particular value in the datom store.  Unique-value attributes can be used in lookup-refs.
154    ///
155    /// `Some(attribute::Unique::Identity)` if this attribute is unique-identity, i.e., it is `:db/unique
156    /// :db.unique/identity`.
157    ///
158    /// Unique-identity attributes always have value type `Ref`.
159    ///
160    /// *Unique-identity* means that the attribute is *unique-value* and that they can be used in
161    /// lookup-refs and will automatically upsert where appropriate.
162    pub unique: Option<attribute::Unique>,
163
164    /// `true` if this attribute is automatically indexed, i.e., it is `:db/indexing true`.
165    pub index: bool,
166
167    /// `true` if this attribute is automatically fulltext indexed, i.e., it is `:db/fulltext true`.
168    ///
169    /// Fulltext attributes always have string values.
170    pub fulltext: bool,
171
172    /// `true` if this attribute is a component, i.e., it is `:db/isComponent true`.
173    ///
174    /// Component attributes always have value type `Ref`.
175    ///
176    /// They are used to compose entities from component sub-entities: they are fetched recursively
177    /// by pull expressions, and they are automatically recursively deleted where appropriate.
178    pub component: bool,
179
180    /// `true` if this attribute doesn't require history to be kept, i.e., it is `:db/noHistory true`.
181    pub no_history: bool,
182}
183
184impl Attribute {
185    /// Combine several attribute flags into a bitfield used in temporary search tables.
186    pub fn flags(&self) -> u8 {
187        let mut flags: u8 = 0;
188
189        if self.index {
190            flags |= AttributeBitFlags::IndexAVET as u8;
191        }
192        if self.value_type == ValueType::Ref {
193            flags |= AttributeBitFlags::IndexVAET as u8;
194        }
195        if self.fulltext {
196            flags |= AttributeBitFlags::IndexFulltext as u8;
197        }
198        if self.unique.is_some() {
199            flags |= AttributeBitFlags::UniqueValue as u8;
200        }
201        flags
202    }
203
204    pub fn to_edn_value(&self, ident: Option<Keyword>) -> edn::Value {
205        let mut attribute_map: BTreeMap<edn::Value, edn::Value> = BTreeMap::default();
206        if let Some(ident) = ident {
207            attribute_map.insert(values::DB_IDENT.clone(), edn::Value::Keyword(ident));
208        }
209
210        attribute_map.insert(
211            values::DB_VALUE_TYPE.clone(),
212            self.value_type.into_edn_value(),
213        );
214
215        attribute_map.insert(
216            values::DB_CARDINALITY.clone(),
217            if self.multival {
218                values::DB_CARDINALITY_MANY.clone()
219            } else {
220                values::DB_CARDINALITY_ONE.clone()
221            },
222        );
223
224        match self.unique {
225            Some(attribute::Unique::Value) => {
226                attribute_map.insert(values::DB_UNIQUE.clone(), values::DB_UNIQUE_VALUE.clone());
227            }
228            Some(attribute::Unique::Identity) => {
229                attribute_map.insert(
230                    values::DB_UNIQUE.clone(),
231                    values::DB_UNIQUE_IDENTITY.clone(),
232                );
233            }
234            None => (),
235        }
236
237        if self.index {
238            attribute_map.insert(values::DB_INDEX.clone(), edn::Value::Boolean(true));
239        }
240
241        if self.fulltext {
242            attribute_map.insert(values::DB_FULLTEXT.clone(), edn::Value::Boolean(true));
243        }
244
245        if self.component {
246            attribute_map.insert(values::DB_IS_COMPONENT.clone(), edn::Value::Boolean(true));
247        }
248
249        if self.no_history {
250            attribute_map.insert(values::DB_NO_HISTORY.clone(), edn::Value::Boolean(true));
251        }
252
253        edn::Value::Map(attribute_map)
254    }
255}
256
257impl Default for Attribute {
258    fn default() -> Attribute {
259        Attribute {
260            // There's no particular reason to favour one value type, so Ref it is.
261            value_type: ValueType::Ref,
262            fulltext: false,
263            index: false,
264            multival: false,
265            unique: None,
266            component: false,
267            no_history: false,
268        }
269    }
270}
271
272/// The attribute of each Mentat assertion has a :db/valueType constraining the value to a
273/// particular set.  Mentat recognizes the following :db/valueType values.
274#[derive(EnumSetType, Debug, Hash, Ord, PartialOrd)]
275#[repr(u32)]
276pub enum ValueType {
277    Ref,
278    Boolean,
279    Instant,
280    Long,
281    Double,
282    String,
283    Keyword,
284    Uuid,
285    Bytes,
286}
287
288impl ValueType {
289    pub fn all_enums() -> EnumSet<ValueType> {
290        // TODO: lazy_static.
291        let mut s = EnumSet::new();
292        s.insert(ValueType::Ref);
293        s.insert(ValueType::Boolean);
294        s.insert(ValueType::Instant);
295        s.insert(ValueType::Long);
296        s.insert(ValueType::Double);
297        s.insert(ValueType::String);
298        s.insert(ValueType::Keyword);
299        s.insert(ValueType::Uuid);
300        s.insert(ValueType::Bytes);
301        s
302    }
303}
304
305impl ValueType {
306    pub fn into_keyword(self) -> Keyword {
307        Keyword::namespaced(
308            "db.type",
309            match self {
310                ValueType::Ref => "ref",
311                ValueType::Boolean => "boolean",
312                ValueType::Instant => "instant",
313                ValueType::Long => "long",
314                ValueType::Double => "double",
315                ValueType::String => "string",
316                ValueType::Keyword => "keyword",
317                ValueType::Uuid => "uuid",
318                ValueType::Bytes => "bytes",
319            },
320        )
321    }
322
323    pub fn from_keyword(keyword: &Keyword) -> Option<Self> {
324        if keyword.namespace() != Some("db.type") {
325            None
326        } else {
327            match keyword.name() {
328                "ref" => Some(ValueType::Ref),
329                "boolean" => Some(ValueType::Boolean),
330                "instant" => Some(ValueType::Instant),
331                "long" => Some(ValueType::Long),
332                "double" => Some(ValueType::Double),
333                "string" => Some(ValueType::String),
334                "keyword" => Some(ValueType::Keyword),
335                "uuid" => Some(ValueType::Uuid),
336                "bytes" => Some(ValueType::Bytes),
337                _ => None,
338            }
339        }
340    }
341
342    pub fn into_typed_value(self) -> TypedValue {
343        TypedValue::typed_ns_keyword(
344            "db.type",
345            match self {
346                ValueType::Ref => "ref",
347                ValueType::Boolean => "boolean",
348                ValueType::Instant => "instant",
349                ValueType::Long => "long",
350                ValueType::Double => "double",
351                ValueType::String => "string",
352                ValueType::Keyword => "keyword",
353                ValueType::Uuid => "uuid",
354                ValueType::Bytes => "bytes",
355            },
356        )
357    }
358
359    pub fn into_edn_value(self) -> edn::Value {
360        match self {
361            ValueType::Ref => values::DB_TYPE_REF.clone(),
362            ValueType::Boolean => values::DB_TYPE_BOOLEAN.clone(),
363            ValueType::Instant => values::DB_TYPE_INSTANT.clone(),
364            ValueType::Long => values::DB_TYPE_LONG.clone(),
365            ValueType::Double => values::DB_TYPE_DOUBLE.clone(),
366            ValueType::String => values::DB_TYPE_STRING.clone(),
367            ValueType::Keyword => values::DB_TYPE_KEYWORD.clone(),
368            ValueType::Uuid => values::DB_TYPE_UUID.clone(),
369            ValueType::Bytes => values::DB_TYPE_BYTES.clone(),
370        }
371    }
372
373    pub fn is_numeric(self) -> bool {
374        matches!(self, ValueType::Long | ValueType::Double)
375    }
376}
377
378impl fmt::Display for ValueType {
379    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
380        write!(
381            f,
382            "{}",
383            match *self {
384                ValueType::Ref => ":db.type/ref",
385                ValueType::Boolean => ":db.type/boolean",
386                ValueType::Instant => ":db.type/instant",
387                ValueType::Long => ":db.type/long",
388                ValueType::Double => ":db.type/double",
389                ValueType::String => ":db.type/string",
390                ValueType::Keyword => ":db.type/keyword",
391                ValueType::Uuid => ":db.type/uuid",
392                ValueType::Bytes => ":db.type/bytes",
393            }
394        )
395    }
396}
397
398/// `TypedValue` is the value type for programmatic use in transaction builders.
399impl TransactableValueMarker for TypedValue {}
400
401/// Represents a value that can be stored in a Mentat store.
402// TODO: expand to include :db.type/uri. https://github.com/mozilla/mentat/issues/201
403// TODO: JSON data type? https://github.com/mozilla/mentat/issues/31
404// TODO: BigInt? Bytes?
405#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq, Serialize, Deserialize)]
406pub enum TypedValue {
407    Ref(Entid),
408    Boolean(bool),
409    Long(i64),
410    Double(OrderedFloat<f64>),
411    Instant(DateTime<Utc>), // Use `into()` to ensure truncation.
412    // TODO: &str throughout?
413    String(ValueRc<String>),
414    Keyword(ValueRc<Keyword>),
415    Uuid(Uuid), // It's only 128 bits, so this should be acceptable to clone.
416    Bytes(Bytes),
417}
418
419impl From<KnownEntid> for TypedValue {
420    fn from(k: KnownEntid) -> TypedValue {
421        TypedValue::Ref(k.0)
422    }
423}
424
425impl TypedValue {
426    /// Returns true if the provided type is `Some` and matches this value's type, or if the
427    /// provided type is `None`.
428    #[inline]
429    pub fn is_congruent_with<T: Into<Option<ValueType>>>(&self, t: T) -> bool {
430        t.into().is_none_or(|x| self.matches_type(x))
431    }
432
433    #[inline]
434    pub fn matches_type(&self, t: ValueType) -> bool {
435        self.value_type() == t
436    }
437
438    pub fn value_type(&self) -> ValueType {
439        match self {
440            TypedValue::Ref(_) => ValueType::Ref,
441            TypedValue::Boolean(_) => ValueType::Boolean,
442            TypedValue::Long(_) => ValueType::Long,
443            TypedValue::Instant(_) => ValueType::Instant,
444            TypedValue::Double(_) => ValueType::Double,
445            TypedValue::String(_) => ValueType::String,
446            TypedValue::Keyword(_) => ValueType::Keyword,
447            TypedValue::Uuid(_) => ValueType::Uuid,
448            TypedValue::Bytes(_) => ValueType::Bytes,
449        }
450    }
451
452    /// Construct a new `TypedValue::Keyword` instance by cloning the provided
453    /// values and wrapping them in a new `ValueRc`. This is expensive, so this might
454    /// be best limited to tests.
455    pub fn typed_ns_keyword<S: AsRef<str>, T: AsRef<str>>(ns: S, name: T) -> TypedValue {
456        Keyword::namespaced(ns.as_ref(), name.as_ref()).into()
457    }
458
459    /// Construct a new `TypedValue::String` instance by cloning the provided
460    /// value and wrapping it in a new `ValueRc`. This is expensive, so this might
461    /// be best limited to tests.
462    pub fn typed_string<S: AsRef<str>>(s: S) -> TypedValue {
463        s.as_ref().into()
464    }
465
466    pub fn current_instant() -> TypedValue {
467        Utc::now().into()
468    }
469
470    /// Construct a new `TypedValue::Instant` instance from the provided
471    /// microsecond timestamp.
472    pub fn instant(micros: i64) -> TypedValue {
473        DateTime::<Utc>::from_micros(micros).into()
474    }
475
476    pub fn into_known_entid(self) -> Option<KnownEntid> {
477        match self {
478            TypedValue::Ref(v) => Some(KnownEntid(v)),
479            _ => None,
480        }
481    }
482
483    pub fn into_entid(self) -> Option<Entid> {
484        match self {
485            TypedValue::Ref(v) => Some(v),
486            _ => None,
487        }
488    }
489
490    pub fn into_kw(self) -> Option<ValueRc<Keyword>> {
491        match self {
492            TypedValue::Keyword(v) => Some(v),
493            _ => None,
494        }
495    }
496
497    pub fn into_boolean(self) -> Option<bool> {
498        match self {
499            TypedValue::Boolean(v) => Some(v),
500            _ => None,
501        }
502    }
503
504    pub fn into_long(self) -> Option<i64> {
505        match self {
506            TypedValue::Long(v) => Some(v),
507            _ => None,
508        }
509    }
510
511    pub fn into_double(self) -> Option<f64> {
512        match self {
513            TypedValue::Double(v) => Some(v.into_inner()),
514            _ => None,
515        }
516    }
517
518    pub fn into_instant(self) -> Option<DateTime<Utc>> {
519        match self {
520            TypedValue::Instant(v) => Some(v),
521            _ => None,
522        }
523    }
524
525    pub fn into_timestamp(self) -> Option<i64> {
526        match self {
527            TypedValue::Instant(v) => Some(v.timestamp()),
528            _ => None,
529        }
530    }
531
532    pub fn into_string(self) -> Option<ValueRc<String>> {
533        match self {
534            TypedValue::String(v) => Some(v),
535            _ => None,
536        }
537    }
538
539    pub fn into_c_string(self) -> Option<*mut c_char> {
540        match self {
541            TypedValue::String(v) => {
542                // Get an independent copy of the string.
543                let s: String = v.cloned();
544
545                // Make a CString out of the new bytes.
546                let c: CString = CString::new(s).expect("String conversion failed!");
547
548                // Return a C-owned pointer.
549                Some(c.into_raw())
550            }
551            _ => None,
552        }
553    }
554
555    pub fn into_kw_c_string(self) -> Option<*mut c_char> {
556        match self {
557            TypedValue::Keyword(v) => {
558                // Get an independent copy of the string.
559                let s: String = v.to_string();
560
561                // Make a CString out of the new bytes.
562                let c: CString = CString::new(s).expect("String conversion failed!");
563
564                // Return a C-owned pointer.
565                Some(c.into_raw())
566            }
567            _ => None,
568        }
569    }
570
571    pub fn into_uuid_c_string(self) -> Option<*mut c_char> {
572        match self {
573            TypedValue::Uuid(v) => {
574                // Get an independent copy of the string.
575                let s: String = v.hyphenated().to_string();
576
577                // Make a CString out of the new bytes.
578                let c: CString = CString::new(s).expect("String conversion failed!");
579
580                // Return a C-owned pointer.
581                Some(c.into_raw())
582            }
583            _ => None,
584        }
585    }
586
587    pub fn into_uuid(self) -> Option<Uuid> {
588        match self {
589            TypedValue::Uuid(v) => Some(v),
590            _ => None,
591        }
592    }
593
594    pub fn into_uuid_string(self) -> Option<String> {
595        match self {
596            TypedValue::Uuid(v) => Some(v.hyphenated().to_string()),
597            _ => None,
598        }
599    }
600
601    pub fn into_bytes(self) -> Option<Bytes> {
602        match self {
603            TypedValue::Bytes(b) => Some(b),
604            _ => None,
605        }
606    }
607}
608
609// We don't do From<i64> or From<Entid> 'cos it's ambiguous.
610
611impl From<bool> for TypedValue {
612    fn from(value: bool) -> TypedValue {
613        TypedValue::Boolean(value)
614    }
615}
616
617/// Truncate the provided `DateTime` to microsecond precision, and return the corresponding
618/// `TypedValue::Instant`.
619impl From<DateTime<Utc>> for TypedValue {
620    fn from(value: DateTime<Utc>) -> TypedValue {
621        TypedValue::Instant(value.microsecond_precision())
622    }
623}
624
625impl From<Uuid> for TypedValue {
626    fn from(value: Uuid) -> TypedValue {
627        TypedValue::Uuid(value)
628    }
629}
630
631impl<'a> From<&'a str> for TypedValue {
632    fn from(value: &'a str) -> TypedValue {
633        TypedValue::String(ValueRc::new(value.to_string()))
634    }
635}
636
637impl From<Arc<String>> for TypedValue {
638    fn from(value: Arc<String>) -> TypedValue {
639        TypedValue::String(ValueRc::from_arc(value))
640    }
641}
642
643impl From<Rc<String>> for TypedValue {
644    fn from(value: Rc<String>) -> TypedValue {
645        TypedValue::String(ValueRc::from_rc(value))
646    }
647}
648
649impl From<Box<String>> for TypedValue {
650    fn from(value: Box<String>) -> TypedValue {
651        TypedValue::String(ValueRc::new(*value))
652    }
653}
654
655impl From<String> for TypedValue {
656    fn from(value: String) -> TypedValue {
657        TypedValue::String(ValueRc::new(value))
658    }
659}
660
661impl From<Arc<Keyword>> for TypedValue {
662    fn from(value: Arc<Keyword>) -> TypedValue {
663        TypedValue::Keyword(ValueRc::from_arc(value))
664    }
665}
666
667impl From<Rc<Keyword>> for TypedValue {
668    fn from(value: Rc<Keyword>) -> TypedValue {
669        TypedValue::Keyword(ValueRc::from_rc(value))
670    }
671}
672
673impl From<Keyword> for TypedValue {
674    fn from(value: Keyword) -> TypedValue {
675        TypedValue::Keyword(ValueRc::new(value))
676    }
677}
678
679impl From<u32> for TypedValue {
680    fn from(value: u32) -> TypedValue {
681        TypedValue::Long(value as i64)
682    }
683}
684
685impl From<i32> for TypedValue {
686    fn from(value: i32) -> TypedValue {
687        TypedValue::Long(value as i64)
688    }
689}
690
691impl From<f64> for TypedValue {
692    fn from(value: f64) -> TypedValue {
693        TypedValue::Double(OrderedFloat(value))
694    }
695}
696
697impl From<&[u8]> for TypedValue {
698    fn from(bslice: &[u8]) -> Self {
699        TypedValue::Bytes(Bytes::copy_from_slice(bslice))
700    }
701}
702
703trait MicrosecondPrecision {
704    /// Truncate the provided `DateTime` to microsecond precision.
705    fn microsecond_precision(self) -> Self;
706}
707
708impl MicrosecondPrecision for DateTime<Utc> {
709    fn microsecond_precision(self) -> DateTime<Utc> {
710        let nanoseconds = self.nanosecond();
711        if nanoseconds.is_multiple_of(1000) {
712            return self;
713        }
714        let microseconds = nanoseconds / 1000;
715        let truncated = microseconds * 1000;
716        #[expect(
717            clippy::expect_used,
718            reason = "timestamp truncation to microseconds should always produce valid values"
719        )]
720        self.with_nanosecond(truncated).expect("valid timestamp")
721    }
722}
723
724/// The values bound in a query specification can be:
725///
726/// * Vecs of structured values, for multi-valued component attributes or nested expressions.
727/// * Single structured values, for single-valued component attributes or nested expressions.
728/// * Single typed values, for simple attributes.
729///
730/// The `Binding` enum defines these three options.
731///
732/// Datomic also supports structured inputs; at present Mentat does not, but this type
733/// would also serve that purpose.
734///
735/// Note that maps are not ordered, and so `Binding` is neither `Ord` nor `PartialOrd`.
736#[derive(Clone, Debug, Eq, PartialEq)]
737pub enum Binding {
738    Scalar(TypedValue),
739    Vec(ValueRc<Vec<Binding>>),
740    Map(ValueRc<StructuredMap>),
741}
742
743impl<T> From<T> for Binding
744where
745    T: Into<TypedValue>,
746{
747    fn from(value: T) -> Self {
748        Binding::Scalar(value.into())
749    }
750}
751
752impl From<StructuredMap> for Binding {
753    fn from(value: StructuredMap) -> Self {
754        Binding::Map(ValueRc::new(value))
755    }
756}
757
758impl From<Vec<Binding>> for Binding {
759    fn from(value: Vec<Binding>) -> Self {
760        Binding::Vec(ValueRc::new(value))
761    }
762}
763
764impl Binding {
765    pub fn into_scalar(self) -> Option<TypedValue> {
766        match self {
767            Binding::Scalar(v) => Some(v),
768            _ => None,
769        }
770    }
771
772    pub fn into_vec(self) -> Option<ValueRc<Vec<Binding>>> {
773        match self {
774            Binding::Vec(v) => Some(v),
775            _ => None,
776        }
777    }
778
779    pub fn into_map(self) -> Option<ValueRc<StructuredMap>> {
780        match self {
781            Binding::Map(v) => Some(v),
782            _ => None,
783        }
784    }
785
786    pub fn as_scalar(&self) -> Option<&TypedValue> {
787        match self {
788            Binding::Scalar(ref v) => Some(v),
789            _ => None,
790        }
791    }
792
793    pub fn as_vec(&self) -> Option<&Vec<Binding>> {
794        match self {
795            Binding::Vec(ref v) => Some(v),
796            _ => None,
797        }
798    }
799
800    pub fn as_map(&self) -> Option<&StructuredMap> {
801        match self {
802            Binding::Map(ref v) => Some(v),
803            _ => None,
804        }
805    }
806}
807
808/// A pull expression expands a binding into a structure. The returned structure
809/// associates attributes named in the input or retrieved from the store with values.
810/// This association is a `StructuredMap`.
811///
812/// Note that 'attributes' in Datomic's case can mean:
813/// - Reversed attribute keywords (:artist/_country).
814/// - An alias using `:as` (:artist/name :as "Band name").
815///
816/// We entirely support the former, and partially support the latter -- you can alias
817/// using a different keyword only.
818#[derive(Clone, Debug, Default, Eq, PartialEq)]
819pub struct StructuredMap(pub IndexMap<ValueRc<Keyword>, Binding>);
820
821impl Deref for StructuredMap {
822    type Target = IndexMap<ValueRc<Keyword>, Binding>;
823
824    fn deref(&self) -> &Self::Target {
825        &self.0
826    }
827}
828
829impl StructuredMap {
830    pub fn insert<N, B>(&mut self, name: N, value: B)
831    where
832        N: Into<ValueRc<Keyword>>,
833        B: Into<Binding>,
834    {
835        self.0.insert(name.into(), value.into());
836    }
837}
838
839impl From<IndexMap<ValueRc<Keyword>, Binding>> for StructuredMap {
840    fn from(src: IndexMap<ValueRc<Keyword>, Binding>) -> Self {
841        StructuredMap(src)
842    }
843}
844
845// Mostly for testing.
846impl<T> From<Vec<(Keyword, T)>> for StructuredMap
847where
848    T: Into<Binding>,
849{
850    fn from(value: Vec<(Keyword, T)>) -> Self {
851        let mut sm = StructuredMap::default();
852        for (k, v) in value.into_iter() {
853            sm.insert(k, v);
854        }
855        sm
856    }
857}
858
859impl Binding {
860    /// Returns true if the provided type is `Some` and matches this value's type, or if the
861    /// provided type is `None`.
862    #[inline]
863    pub fn is_congruent_with<T: Into<Option<ValueType>>>(&self, t: T) -> bool {
864        t.into().is_none_or(|x| self.matches_type(x))
865    }
866
867    #[inline]
868    pub fn matches_type(&self, t: ValueType) -> bool {
869        self.value_type() == Some(t)
870    }
871
872    pub fn value_type(&self) -> Option<ValueType> {
873        match self {
874            Binding::Scalar(ref v) => Some(v.value_type()),
875
876            Binding::Map(_) => None,
877            Binding::Vec(_) => None,
878        }
879    }
880}
881
882/// Return the current time as a UTC `DateTime` instance with microsecond precision.
883pub fn now() -> DateTime<Utc> {
884    Utc::now().microsecond_precision()
885}
886
887impl Binding {
888    pub fn into_known_entid(self) -> Option<KnownEntid> {
889        match self {
890            Binding::Scalar(TypedValue::Ref(v)) => Some(KnownEntid(v)),
891            _ => None,
892        }
893    }
894
895    pub fn into_entid(self) -> Option<Entid> {
896        match self {
897            Binding::Scalar(TypedValue::Ref(v)) => Some(v),
898            _ => None,
899        }
900    }
901
902    pub fn into_kw(self) -> Option<ValueRc<Keyword>> {
903        match self {
904            Binding::Scalar(TypedValue::Keyword(v)) => Some(v),
905            _ => None,
906        }
907    }
908
909    pub fn into_boolean(self) -> Option<bool> {
910        match self {
911            Binding::Scalar(TypedValue::Boolean(v)) => Some(v),
912            _ => None,
913        }
914    }
915
916    pub fn into_long(self) -> Option<i64> {
917        match self {
918            Binding::Scalar(TypedValue::Long(v)) => Some(v),
919            _ => None,
920        }
921    }
922
923    pub fn into_double(self) -> Option<f64> {
924        match self {
925            Binding::Scalar(TypedValue::Double(v)) => Some(v.into_inner()),
926            _ => None,
927        }
928    }
929
930    pub fn into_instant(self) -> Option<DateTime<Utc>> {
931        match self {
932            Binding::Scalar(TypedValue::Instant(v)) => Some(v),
933            _ => None,
934        }
935    }
936
937    pub fn into_timestamp(self) -> Option<i64> {
938        match self {
939            Binding::Scalar(TypedValue::Instant(v)) => Some(v.timestamp()),
940            _ => None,
941        }
942    }
943
944    pub fn into_string(self) -> Option<ValueRc<String>> {
945        match self {
946            Binding::Scalar(TypedValue::String(v)) => Some(v),
947            _ => None,
948        }
949    }
950
951    pub fn into_uuid(self) -> Option<Uuid> {
952        match self {
953            Binding::Scalar(TypedValue::Uuid(v)) => Some(v),
954            _ => None,
955        }
956    }
957
958    pub fn into_uuid_string(self) -> Option<String> {
959        match self {
960            Binding::Scalar(TypedValue::Uuid(v)) => Some(v.hyphenated().to_string()),
961            _ => None,
962        }
963    }
964
965    pub fn into_c_string(self) -> Option<*mut c_char> {
966        match self {
967            Binding::Scalar(v) => v.into_c_string(),
968            _ => None,
969        }
970    }
971
972    pub fn into_kw_c_string(self) -> Option<*mut c_char> {
973        match self {
974            Binding::Scalar(v) => v.into_kw_c_string(),
975            _ => None,
976        }
977    }
978
979    pub fn into_uuid_c_string(self) -> Option<*mut c_char> {
980        match self {
981            Binding::Scalar(v) => v.into_uuid_c_string(),
982            _ => None,
983        }
984    }
985
986    pub fn as_entid(&self) -> Option<&Entid> {
987        match self {
988            Binding::Scalar(TypedValue::Ref(ref v)) => Some(v),
989            _ => None,
990        }
991    }
992
993    pub fn as_kw(&self) -> Option<&ValueRc<Keyword>> {
994        match self {
995            Binding::Scalar(TypedValue::Keyword(ref v)) => Some(v),
996            _ => None,
997        }
998    }
999
1000    pub fn as_boolean(&self) -> Option<&bool> {
1001        match self {
1002            Binding::Scalar(TypedValue::Boolean(ref v)) => Some(v),
1003            _ => None,
1004        }
1005    }
1006
1007    pub fn as_long(&self) -> Option<&i64> {
1008        match self {
1009            Binding::Scalar(TypedValue::Long(ref v)) => Some(v),
1010            _ => None,
1011        }
1012    }
1013
1014    pub fn as_double(&self) -> Option<&f64> {
1015        match self {
1016            Binding::Scalar(TypedValue::Double(ref v)) => Some(&v.0),
1017            _ => None,
1018        }
1019    }
1020
1021    pub fn as_instant(&self) -> Option<&DateTime<Utc>> {
1022        match self {
1023            Binding::Scalar(TypedValue::Instant(ref v)) => Some(v),
1024            _ => None,
1025        }
1026    }
1027
1028    pub fn as_string(&self) -> Option<&ValueRc<String>> {
1029        match self {
1030            Binding::Scalar(TypedValue::String(ref v)) => Some(v),
1031            _ => None,
1032        }
1033    }
1034
1035    pub fn as_uuid(&self) -> Option<&Uuid> {
1036        match self {
1037            Binding::Scalar(TypedValue::Uuid(ref v)) => Some(v),
1038            _ => None,
1039        }
1040    }
1041}
1042
1043#[test]
1044fn test_typed_value() {
1045    assert!(TypedValue::Boolean(false).is_congruent_with(None));
1046    assert!(TypedValue::Boolean(false).is_congruent_with(ValueType::Boolean));
1047    assert!(!TypedValue::typed_string("foo").is_congruent_with(ValueType::Boolean));
1048    assert!(TypedValue::typed_string("foo").is_congruent_with(ValueType::String));
1049    assert!(TypedValue::typed_string("foo").is_congruent_with(None));
1050}
1051
1052#[cfg(test)]
1053mod tests {
1054    use super::*;
1055
1056    #[test]
1057    fn test_attribute_flags() {
1058        let attr1 = Attribute {
1059            index: true,
1060            value_type: ValueType::Ref,
1061            fulltext: false,
1062            unique: None,
1063            multival: false,
1064            component: false,
1065            no_history: false,
1066        };
1067
1068        assert!(attr1.flags() & AttributeBitFlags::IndexAVET as u8 != 0);
1069        assert!(attr1.flags() & AttributeBitFlags::IndexVAET as u8 != 0);
1070        assert!(attr1.flags() & AttributeBitFlags::IndexFulltext as u8 == 0);
1071        assert!(attr1.flags() & AttributeBitFlags::UniqueValue as u8 == 0);
1072
1073        let attr2 = Attribute {
1074            index: false,
1075            value_type: ValueType::Boolean,
1076            fulltext: true,
1077            unique: Some(attribute::Unique::Value),
1078            multival: false,
1079            component: false,
1080            no_history: false,
1081        };
1082
1083        assert!(attr2.flags() & AttributeBitFlags::IndexAVET as u8 == 0);
1084        assert!(attr2.flags() & AttributeBitFlags::IndexVAET as u8 == 0);
1085        assert!(attr2.flags() & AttributeBitFlags::IndexFulltext as u8 != 0);
1086        assert!(attr2.flags() & AttributeBitFlags::UniqueValue as u8 != 0);
1087
1088        let attr3 = Attribute {
1089            index: false,
1090            value_type: ValueType::Boolean,
1091            fulltext: true,
1092            unique: Some(attribute::Unique::Identity),
1093            multival: false,
1094            component: false,
1095            no_history: false,
1096        };
1097
1098        assert!(attr3.flags() & AttributeBitFlags::IndexAVET as u8 == 0);
1099        assert!(attr3.flags() & AttributeBitFlags::IndexVAET as u8 == 0);
1100        assert!(attr3.flags() & AttributeBitFlags::IndexFulltext as u8 != 0);
1101        assert!(attr3.flags() & AttributeBitFlags::UniqueValue as u8 != 0);
1102    }
1103}