typeline_core/record_data/
field_value.rs

1use std::{any::Any, fmt::Display, mem::ManuallyDrop};
2
3use indexmap::IndexMap;
4use metamatch::metamatch;
5use num::{BigInt, BigRational, FromPrimitive, ToPrimitive};
6
7use crate::{
8    cli::call_expr::Argument,
9    operators::errors::OperatorApplicationError,
10    record_data::field_value_ref::FieldValueSlice,
11    utils::{
12        force_cast,
13        maybe_text::{MaybeText, MaybeTextRef},
14        string_store::StringStoreEntry,
15        text_write::MaybeTextWrite,
16    },
17};
18
19use super::{
20    array::Array,
21    custom_data::CustomDataBox,
22    field::FieldRefOffset,
23    field_data::{FieldValueRepr, FieldValueType, FixedSizeFieldValueType},
24    field_value_ref::{FieldValueRef, FieldValueRefMut},
25    formattable::{Formattable, FormattingContext},
26    scope_manager::OpDeclRef,
27    stream_value::StreamValueId,
28};
29
30// the different logical data types
31// irrespective of representation in memory, see FieldDataRepr for that
32
33#[derive(PartialEq, Eq, Clone, Copy, Debug)]
34pub enum FieldValueKind {
35    Undefined,
36    Null,
37    Int,
38    Float,
39    Error,
40    Bytes,
41    Text,
42    Object,
43    Array,
44    Argument,
45    OpDecl,
46    FieldReference,
47    SlicedFieldReference,
48    StreamValueId,
49    Custom,
50}
51
52impl Display for FieldValueKind {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        f.write_str(self.to_str())
55    }
56}
57
58#[derive(Debug, Clone, Default)]
59pub enum FieldValue {
60    #[default]
61    Undefined,
62    Null,
63    Int(i64),
64    BigInt(Box<BigInt>),
65    Float(f64),
66    BigRational(Box<BigRational>),
67    // PERF: better to use a custom version of Arc<String> for this with only
68    // one indirection (and no weak refs) that still stores the capacity
69    // (unlike `Arc<str>`). Maybe that type stores the meta info at
70    // the end so we can convert to `String` without realloc or memcpy
71    Text(String),
72    Bytes(Vec<u8>), // TODO: same as for `Text`
73    // this is the only field that's allowed to be 32 bytes large
74    // this still keeps FieldValue at 32 bytes due to Rust's
75    // cool enum layout optimizations
76    Array(Array),
77    Object(Box<Object>),
78    Custom(CustomDataBox),
79    Error(OperatorApplicationError),
80    OpDecl(OpDeclRef),
81    Argument(Box<Argument>),
82    StreamValueId(StreamValueId),
83    FieldReference(FieldReference),
84    SlicedFieldReference(SlicedFieldReference),
85}
86
87#[derive(Debug, Clone, Default)]
88pub enum FieldValueUnboxed {
89    #[default]
90    Undefined,
91    Null,
92    Int(i64),
93    BigInt(BigInt),
94    Float(f64),
95    BigRational(BigRational),
96    // PERF: better to use a custom version of Arc<String> for this with only
97    // one indirection (and no weak refs) that still stores the capacity
98    // (unlike `Arc<str>`). Maybe that type stores the meta info at
99    // the end so we can convert to `String` without realloc or memcpy
100    Text(String),
101    Bytes(Vec<u8>), // TODO: same as for `Text`
102    // this is the only field that's allowed to be 32 bytes large
103    // this still keeps FieldValue at 32 bytes due to Rust's
104    // cool enum layout optimizations
105    Array(Array),
106    Object(Object),
107    Custom(CustomDataBox),
108    Error(OperatorApplicationError),
109    OpDecl(OpDeclRef),
110    Argument(Argument),
111    StreamValueId(StreamValueId),
112    FieldReference(FieldReference),
113    SlicedFieldReference(SlicedFieldReference),
114}
115
116#[derive(Clone, Copy, Debug, PartialEq, Eq)]
117pub struct Null;
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119pub struct Undefined;
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121pub struct GroupSeparator;
122
123pub type ObjectKeysStored = IndexMap<String, FieldValue>;
124pub type ObjectKeysInterned = IndexMap<StringStoreEntry, FieldValue>;
125
126#[derive(Debug, Clone, PartialEq)]
127pub enum Object {
128    KeysStored(ObjectKeysStored),
129    KeysInterned(ObjectKeysInterned),
130}
131
132#[derive(Debug, Copy, Clone, PartialEq)]
133pub struct FieldReference {
134    pub field_ref_offset: FieldRefOffset,
135}
136
137#[derive(Debug, Copy, Clone, PartialEq)]
138pub struct SlicedFieldReference {
139    pub field_ref_offset: FieldRefOffset,
140    pub begin: usize,
141    pub end: usize,
142}
143
144impl SlicedFieldReference {
145    pub fn new(
146        field_ref_offset: FieldRefOffset,
147        begin: usize,
148        end: usize,
149    ) -> Self {
150        Self {
151            field_ref_offset,
152            begin,
153            end,
154        }
155    }
156}
157impl FieldReference {
158    pub fn new(field_ref_offset: FieldRefOffset) -> Self {
159        Self { field_ref_offset }
160    }
161}
162
163impl Default for Object {
164    fn default() -> Self {
165        Object::KeysStored(IndexMap::default())
166    }
167}
168
169impl Object {
170    pub fn new_keys_stored() -> Object {
171        Object::KeysStored(IndexMap::default())
172    }
173    pub fn len(&self) -> usize {
174        match self {
175            Object::KeysStored(d) => d.len(),
176            Object::KeysInterned(d) => d.len(),
177        }
178    }
179    pub fn is_empty(&self) -> bool {
180        self.len() == 0
181    }
182
183    pub fn clear(&mut self) {
184        match self {
185            Object::KeysStored(o) => o.clear(),
186            Object::KeysInterned(o) => o.clear(),
187        }
188    }
189    pub fn push_stored_key(&mut self, key: String, value: FieldValue) {
190        if let Object::KeysStored(o) = self {
191            o.insert(key, value);
192        } else {
193            unreachable!()
194        }
195    }
196}
197
198impl FromIterator<(String, FieldValue)> for Object {
199    fn from_iter<I: IntoIterator<Item = (String, FieldValue)>>(
200        iter: I,
201    ) -> Self {
202        Object::KeysStored(IndexMap::from_iter(iter))
203    }
204}
205
206impl FieldValueKind {
207    pub fn repr(&self, inline: bool) -> FieldValueRepr {
208        metamatch!(match self {
209            #[expand((KIND, INLINE, BUFFERED) in [
210                (Text, TextInline, TextBuffer),
211                (Bytes, BytesInline, BytesBuffer),
212                (Int, Int, BigInt),
213                (Float, Float, BigRational)
214            ])]
215            FieldValueKind::KIND => {
216                if inline {
217                    FieldValueRepr::INLINE
218                } else {
219                    FieldValueRepr::BUFFERED
220                }
221            }
222
223            #[expand(T in [
224                Undefined, Null, Error, Object, Array,
225                Argument, OpDecl, FieldReference,
226                SlicedFieldReference, StreamValueId, Custom
227            ])]
228            FieldValueKind::T => FieldValueRepr::T,
229        })
230    }
231    pub fn to_str(self) -> &'static str {
232        match self {
233            FieldValueKind::Undefined => "undefined",
234            FieldValueKind::Null => "null",
235            FieldValueKind::Int => "int",
236            FieldValueKind::Float => "float",
237            FieldValueKind::Error => "error",
238            FieldValueKind::Text => "str",
239            FieldValueKind::Bytes => "bytes",
240            FieldValueKind::Custom => "custom",
241            FieldValueKind::Object => "object",
242            FieldValueKind::Array => "array",
243            FieldValueKind::FieldReference => "field_reference",
244            FieldValueKind::SlicedFieldReference => "sliced_field_reference",
245            FieldValueKind::StreamValueId => "stream_value_id",
246            FieldValueKind::Argument => "argument",
247            FieldValueKind::OpDecl => "macro",
248        }
249    }
250    pub fn is_valid_utf8(self) -> bool {
251        match self {
252            FieldValueKind::Undefined
253            | FieldValueKind::Null
254            | FieldValueKind::Int
255            | FieldValueKind::Float
256            | FieldValueKind::Error
257            | FieldValueKind::Text
258            | FieldValueKind::Object
259            | FieldValueKind::Array
260            | FieldValueKind::OpDecl => true,
261            FieldValueKind::Bytes
262            | FieldValueKind::FieldReference
263            | FieldValueKind::SlicedFieldReference
264            | FieldValueKind::StreamValueId
265            | FieldValueKind::Custom
266            | FieldValueKind::Argument => false,
267        }
268    }
269}
270
271impl PartialEq for FieldValue {
272    fn eq(&self, other: &Self) -> bool {
273        metamatch!(match self {
274            #[expand(REP in [Null, Undefined])]
275            Self::REP => matches!(other, Self::REP),
276
277            #[expand(REP in [
278                Int, Error, Array, Object, Bytes, Text, OpDecl,
279                FieldReference, SlicedFieldReference, Custom, Float,
280                StreamValueId, BigInt, BigRational, Argument
281            ])]
282            FieldValue::REP(l) => {
283                matches!(other, FieldValue::REP(r) if r == l)
284            }
285        })
286    }
287}
288
289impl FieldValue {
290    pub fn format(
291        &self,
292        ctx: &mut FormattingContext,
293        w: &mut impl MaybeTextWrite,
294    ) -> Result<(), std::io::Error> {
295        self.as_ref().format(ctx, w)
296    }
297    pub fn repr(&self) -> FieldValueRepr {
298        metamatch!(match self {
299            FieldValue::Null => FieldValueRepr::Null,
300            FieldValue::Undefined => FieldValueRepr::Undefined,
301
302            FieldValue::Text(_) => FieldValueRepr::TextBuffer,
303            FieldValue::Bytes(_) => FieldValueRepr::BytesBuffer,
304
305            #[expand(REP in [
306                Int, Error, Array, Object, OpDecl,
307                FieldReference, SlicedFieldReference, Custom, Float,
308                StreamValueId, BigInt, BigRational, Argument
309            ])]
310            FieldValue::REP(_) => FieldValueRepr::REP,
311        })
312    }
313    pub fn downcast_ref<R: FieldValueType>(&self) -> Option<&R> {
314        metamatch!(match self {
315            #[expand(REP in [Null, Undefined])]
316            FieldValue::REP => <dyn Any>::downcast_ref(&REP),
317
318            #[expand(REP in [
319                Int, Error, Array, Object, Text, Bytes, OpDecl,
320                FieldReference, SlicedFieldReference, Custom, Float,
321                StreamValueId, BigInt, BigRational, Argument
322            ])]
323            FieldValue::REP(v) => {
324                if R::FIELD_VALUE_BOXED {
325                    <dyn Any>::downcast_ref::<Box<R>>(v).map(|v| &**v)
326                } else {
327                    <dyn Any>::downcast_ref(v)
328                }
329            }
330        })
331    }
332    pub fn downcast_mut<R: FieldValueType>(&mut self) -> Option<&mut R> {
333        metamatch!(match self {
334            #[expand(T in [Null, Undefined])]
335            v @ FieldValue::T => <dyn Any>::downcast_mut(v),
336
337            #[expand(REP in [
338                Int, Error, Array, Object, Text, Bytes, OpDecl,
339                FieldReference, SlicedFieldReference, Custom, Float,
340                StreamValueId, BigInt, BigRational, Argument
341            ])]
342            FieldValue::REP(v) => {
343                if R::FIELD_VALUE_BOXED {
344                    <dyn Any>::downcast_mut::<Box<R>>(v).map(|v| &mut **v)
345                } else {
346                    <dyn Any>::downcast_mut(v)
347                }
348            }
349        })
350    }
351    pub fn as_maybe_text_ref(&self) -> Option<MaybeTextRef> {
352        match self {
353            FieldValue::Text(v) => Some(MaybeTextRef::Text(v)),
354            FieldValue::Bytes(v) => Some(MaybeTextRef::Bytes(v)),
355            FieldValue::Argument(v) => v.value.as_maybe_text_ref(),
356            FieldValue::Undefined
357            | FieldValue::Null
358            | FieldValue::Int(_)
359            | FieldValue::BigInt(_)
360            | FieldValue::Float(_)
361            | FieldValue::BigRational(_)
362            | FieldValue::Array(_)
363            | FieldValue::OpDecl(_)
364            | FieldValue::Object(_)
365            | FieldValue::Custom(_)
366            | FieldValue::Error(_)
367            | FieldValue::StreamValueId(_)
368            | FieldValue::FieldReference(_)
369            | FieldValue::SlicedFieldReference(_) => None,
370        }
371    }
372    pub fn text_or_bytes(&self) -> Option<&[u8]> {
373        self.as_maybe_text_ref().map(|t| t.as_bytes())
374    }
375    pub fn downcast<R: FixedSizeFieldValueType>(self) -> Option<R> {
376        let mut this = ManuallyDrop::new(self);
377        this.downcast_mut().map(|v| unsafe { std::ptr::read(v) })
378    }
379    pub fn downcast_allowing_text_as_bytes<R: FixedSizeFieldValueType>(
380        self,
381    ) -> Option<R> {
382        if let FieldValue::Text(text) = self {
383            if R::REPR != FieldValueRepr::TextBuffer {
384                return FieldValue::Bytes(text.into_bytes()).downcast();
385            }
386            return FieldValue::Text(text).downcast();
387        }
388        let mut this = ManuallyDrop::new(self);
389        this.downcast_mut().map(|v| unsafe { std::ptr::read(v) })
390    }
391    pub fn as_ref(&self) -> FieldValueRef {
392        metamatch!(match self {
393            #[expand(REP in [Null, Undefined])]
394            FieldValue::REP => FieldValueRef::REP,
395
396            #[expand(REP in [
397                Int, Error, Array, Object, Text, Bytes,
398                FieldReference, SlicedFieldReference, Custom, Float,
399                StreamValueId, BigInt, BigRational, Argument, OpDecl
400            ])]
401            FieldValue::REP(v) => FieldValueRef::REP(v),
402        })
403    }
404    // This is different from `.as_ref().as_slice()` as it is able to use
405    // `TextBuffer`/`BytesBuffer`, which get lost in translation when using
406    // `as_ref()`
407    pub fn as_slice(&self) -> FieldValueSlice {
408        metamatch!(match self {
409            #[expand(REP in [Null, Undefined])]
410            FieldValue::REP => FieldValueSlice::REP(1),
411
412            #[expand(REP in [
413                Int, Error, Array, Object, OpDecl,
414                FieldReference, SlicedFieldReference, Custom, Float,
415                StreamValueId, BigInt, BigRational, Argument,
416            ])]
417            FieldValue::REP(v) =>
418                FieldValueSlice::REP(std::slice::from_ref(v)),
419
420            #[expand((REP, TGT) in [
421                (Text, TextBuffer),
422                (Bytes, BytesBuffer)])
423            ]
424            FieldValue::REP(v) =>
425                FieldValueSlice::TGT(std::slice::from_ref(v)),
426        })
427    }
428    pub fn as_ref_mut(&mut self) -> FieldValueRefMut {
429        metamatch!(match self {
430            #[expand(REP in [Null, Undefined])]
431            FieldValue::REP => FieldValueRefMut::REP,
432
433            #[expand(REP in [
434                Int, Error, Array, Object,
435                FieldReference, SlicedFieldReference, Custom, Float,
436                StreamValueId, BigInt, BigRational, Argument, OpDecl
437            ])]
438            FieldValue::REP(v) => FieldValueRefMut::REP(v),
439
440            #[expand((VALUE_T, REF_T) in [
441                (Text, TextBuffer),
442                (Bytes, BytesBuffer)
443            ])]
444            FieldValue::VALUE_T(v) => FieldValueRefMut::REF_T(v),
445        })
446    }
447    pub fn from_maybe_text(t: MaybeText) -> Self {
448        match t {
449            MaybeText::Text(t) => FieldValue::Text(t),
450            MaybeText::Bytes(b) => FieldValue::Bytes(b),
451        }
452    }
453    pub fn into_maybe_text(self) -> Option<MaybeText> {
454        match self {
455            FieldValue::Text(t) => Some(MaybeText::Text(t)),
456            FieldValue::Bytes(b) => Some(MaybeText::Bytes(b)),
457            FieldValue::Argument(arg) => arg.value.into_maybe_text(),
458            _ => None,
459        }
460    }
461    pub fn from_fixed_sized_type<T: FixedSizeFieldValueType>(v: T) -> Self {
462        // SAFETY: We *know* that `T` and `Q` will be *identical* because of
463        // the check on `T::REP`. `FixedSizeFieldValueType` is an
464        // unsafe trait, so assuming that nobody gave us an incorrect
465        // `REP` is sound.
466        metamatch!(match T::REPR {
467            #[expand(REP in [Null, Undefined])]
468            FieldValueRepr::REP => FieldValue::REP,
469
470            #[expand(REP in [
471                Int, Error, Array, Object,
472                FieldReference, SlicedFieldReference, Custom, Float,
473                StreamValueId, BigInt, BigRational, Argument, OpDecl
474            ])]
475            FieldValueRepr::REP => {
476                if T::FIELD_VALUE_BOXED {
477                    FieldValue::REP(unsafe { force_cast(Box::new(v)) })
478                } else {
479                    FieldValue::REP(unsafe { force_cast(v) })
480                }
481            }
482
483            #[expand((REP_T, VALUE_T) in [
484                (TextBuffer, Text),
485                (BytesBuffer, Bytes)
486            ])]
487            FieldValueRepr::REP_T => {
488                FieldValue::VALUE_T(unsafe { force_cast(v) })
489            }
490
491            // not fixed size types
492            FieldValueRepr::TextInline | FieldValueRepr::BytesInline => {
493                unreachable!()
494            }
495        })
496    }
497    pub fn is_error(&self) -> bool {
498        matches!(self, FieldValue::Error(_))
499    }
500    pub fn kind(&self) -> FieldValueKind {
501        self.repr().kind()
502    }
503    pub fn is_valid_utf8(&self) -> bool {
504        self.kind().is_valid_utf8()
505    }
506    pub fn try_cast_int(&self, fuzzy: bool) -> Option<i64> {
507        match self {
508            FieldValue::Text(_)
509            | FieldValue::Bytes(_)
510            | FieldValue::Float(_)
511                if !fuzzy =>
512            {
513                None
514            }
515
516            FieldValue::Int(v) => Some(*v),
517            FieldValue::BigInt(v) => v.to_i64(),
518
519            #[allow(clippy::cast_precision_loss, clippy::float_cmp)]
520            &FieldValue::Float(f) => {
521                let int = f as i64;
522                if int as f64 == f {
523                    return Some(int);
524                }
525                None
526            }
527
528            FieldValue::Text(v) => v.parse().ok(),
529            FieldValue::Bytes(v) => std::str::from_utf8(v).ok()?.parse().ok(),
530
531            FieldValue::BigRational(v) => {
532                if !v.is_integer() {
533                    return None;
534                }
535                v.numer().to_i64()
536            }
537
538            FieldValue::Argument(v) => v.value.try_cast_int(fuzzy),
539
540            FieldValue::Undefined
541            | FieldValue::Null
542            | FieldValue::Array(_)
543            | FieldValue::Object(_)
544            | FieldValue::OpDecl(_)
545            | FieldValue::Custom(_)
546            | FieldValue::Error(_)
547            | FieldValue::StreamValueId(_)
548            | FieldValue::FieldReference(_)
549            | FieldValue::SlicedFieldReference(_) => None,
550        }
551    }
552    pub fn try_into_bigint(self, fuzzy: bool) -> Option<BigInt> {
553        match self {
554            FieldValue::Text(_)
555            | FieldValue::Bytes(_)
556            | FieldValue::Float(_)
557                if !fuzzy =>
558            {
559                None
560            }
561
562            FieldValue::Int(v) => Some(BigInt::from(v)),
563            FieldValue::BigInt(v) => Some(*v),
564            FieldValue::Float(f) => {
565                if let Some(v) = BigInt::from_f64(f) {
566                    if v.to_f64() == Some(f) {
567                        return Some(v);
568                    }
569                }
570                None
571            }
572            FieldValue::Text(v) => v.parse().ok(),
573            FieldValue::Bytes(v) => std::str::from_utf8(&v).ok()?.parse().ok(),
574            FieldValue::BigRational(v) => {
575                if !v.is_integer() {
576                    return None;
577                }
578                let (numer, _) = v.into_raw();
579                Some(numer)
580            }
581            FieldValue::Argument(v) => v.value.try_into_bigint(fuzzy),
582            FieldValue::Undefined
583            | FieldValue::Null
584            | FieldValue::Array(_)
585            | FieldValue::Object(_)
586            | FieldValue::Custom(_)
587            | FieldValue::Error(_)
588            | FieldValue::OpDecl(_)
589            | FieldValue::StreamValueId(_)
590            | FieldValue::FieldReference(_)
591            | FieldValue::SlicedFieldReference(_) => None,
592        }
593    }
594    pub fn unbox(self) -> FieldValueUnboxed {
595        metamatch!(match self {
596            #[expand(REP in [Null, Undefined])]
597            FieldValue::REP => FieldValueUnboxed::REP,
598
599            #[expand(REP in [
600                Int, Error, Array, OpDecl, Text, Bytes,
601                FieldReference, SlicedFieldReference, Custom, Float,
602                StreamValueId,
603            ])]
604            FieldValue::REP(v) => FieldValueUnboxed::REP(v),
605
606            #[expand(REP in [
607                Object, BigInt, BigRational, Argument,
608            ])]
609            FieldValue::REP(v) => FieldValueUnboxed::REP(*v),
610        })
611    }
612
613    pub fn deref_argument(&self) -> &FieldValue {
614        if let FieldValue::Argument(arg) = self {
615            &arg.value
616        } else {
617            self
618        }
619    }
620    pub fn deref_argument_mut(&mut self) -> &mut FieldValue {
621        if let FieldValue::Argument(arg) = self {
622            &mut arg.value
623        } else {
624            self
625        }
626    }
627}
628
629impl FieldValueUnboxed {
630    pub fn into_field_value(self) -> FieldValue {
631        metamatch!(match self {
632            #[expand(REP in [Null, Undefined])]
633            FieldValueUnboxed::REP => FieldValue::REP,
634
635            #[expand(REP in [
636                Int, Error, Array, OpDecl, Text, Bytes,
637                FieldReference, SlicedFieldReference, Custom, Float,
638                StreamValueId,
639            ])]
640            FieldValueUnboxed::REP(v) => FieldValue::REP(v),
641
642            #[expand(REP in [
643                Object, BigInt, BigRational, Argument,
644            ])]
645            FieldValueUnboxed::REP(v) => FieldValue::REP(Box::new(v)),
646        })
647    }
648}