re_types_core/
tuid.rs

1use std::sync::Arc;
2
3use arrow::{
4    array::{ArrayRef, StructArray, UInt64Array},
5    datatypes::{DataType, Field, Fields},
6};
7
8use re_arrow_util::ArrowArrayDowncastRef as _;
9use re_tuid::Tuid;
10
11use crate::{DeserializationError, Loggable};
12
13// ---
14
15// TODO(emilk): This is a bit ugly… but good enough for now?
16pub fn tuid_arrow_fields() -> Fields {
17    Fields::from(vec![
18        Field::new("time_ns", DataType::UInt64, false),
19        Field::new("inc", DataType::UInt64, false),
20    ])
21}
22
23impl Loggable for Tuid {
24    #[inline]
25    fn arrow_datatype() -> arrow::datatypes::DataType {
26        DataType::Struct(tuid_arrow_fields())
27    }
28
29    fn to_arrow_opt<'a>(
30        _data: impl IntoIterator<Item = Option<impl Into<std::borrow::Cow<'a, Self>>>>,
31    ) -> crate::SerializationResult<ArrayRef>
32    where
33        Self: 'a,
34    {
35        Err(crate::SerializationError::not_implemented(
36            Self::ARROW_EXTENSION_NAME,
37            "TUIDs are never nullable, use `to_arrow()` instead",
38        ))
39    }
40
41    #[inline]
42    fn to_arrow<'a>(
43        data: impl IntoIterator<Item = impl Into<std::borrow::Cow<'a, Self>>>,
44    ) -> crate::SerializationResult<ArrayRef>
45    where
46        Self: 'a,
47    {
48        let (time_ns_values, inc_values): (Vec<_>, Vec<_>) = data
49            .into_iter()
50            .map(Into::into)
51            .map(|tuid| (tuid.nanoseconds_since_epoch(), tuid.inc()))
52            .unzip();
53
54        let values: Vec<ArrayRef> = vec![
55            Arc::new(UInt64Array::from(time_ns_values)),
56            Arc::new(UInt64Array::from(inc_values)),
57        ];
58        let validity = None;
59
60        Ok(Arc::new(StructArray::new(
61            Fields::from(vec![
62                Field::new("time_ns", DataType::UInt64, false),
63                Field::new("inc", DataType::UInt64, false),
64            ]),
65            values,
66            validity,
67        )))
68    }
69
70    fn from_arrow(array: &dyn ::arrow::array::Array) -> crate::DeserializationResult<Vec<Self>> {
71        let expected_datatype = Self::arrow_datatype();
72        let actual_datatype = array.data_type();
73        if actual_datatype != &expected_datatype {
74            return Err(DeserializationError::datatype_mismatch(
75                expected_datatype,
76                actual_datatype.clone(),
77            ));
78        }
79
80        // NOTE: Unwrap is safe everywhere below, datatype is checked above.
81        // NOTE: We don't even look at the validity, our datatype says we don't care.
82
83        #[allow(clippy::unwrap_used)]
84        let array = array.downcast_array_ref::<StructArray>().unwrap();
85
86        // TODO(cmc): Can we rely on the fields ordering from the datatype? I would assume not
87        // since we generally cannot rely on anything when it comes to arrow…
88        // If we could, that would also impact our codegen deserialization path.
89        let (time_ns_index, inc_index) = {
90            let mut time_ns_index = None;
91            let mut inc_index = None;
92            for (i, field) in array.fields().iter().enumerate() {
93                if field.name() == "time_ns" {
94                    time_ns_index = Some(i);
95                } else if field.name() == "inc" {
96                    inc_index = Some(i);
97                }
98            }
99            #[allow(clippy::unwrap_used)]
100            (time_ns_index.unwrap(), inc_index.unwrap())
101        };
102
103        let get_buffer = |field_index: usize| {
104            #[allow(clippy::unwrap_used)]
105            array.columns()[field_index]
106                .downcast_array_ref::<UInt64Array>()
107                .unwrap()
108                .values()
109        };
110
111        let time_ns_buffer = get_buffer(time_ns_index);
112        let inc_buffer = get_buffer(inc_index);
113
114        if time_ns_buffer.len() != inc_buffer.len() {
115            return Err(DeserializationError::mismatched_struct_field_lengths(
116                "time_ns",
117                time_ns_buffer.len(),
118                "inc",
119                inc_buffer.len(),
120            ));
121        }
122
123        Ok(time_ns_buffer
124            .iter()
125            .copied()
126            .zip(inc_buffer.iter().copied())
127            .map(|(time_ns, inc)| Self::from_nanos_and_inc(time_ns, inc))
128            .collect())
129    }
130}
131
132/// Implements [`crate::Component`] for any given type that is a simple wrapper
133/// (newtype) around a [`Tuid`].
134///
135/// Usage:
136/// ```ignore
137/// re_types_core::delegate_arrow_tuid!(RowId);
138/// ```
139#[macro_export]
140macro_rules! delegate_arrow_tuid {
141    ($typ:ident as $fqname:expr) => {
142        $crate::macros::impl_into_cow!($typ);
143
144        impl $crate::Loggable for $typ {
145            #[inline]
146            fn arrow_datatype() -> ::arrow::datatypes::DataType {
147                $crate::external::re_tuid::Tuid::arrow_datatype()
148            }
149
150            #[inline]
151            fn to_arrow_opt<'a>(
152                _values: impl IntoIterator<Item = Option<impl Into<::std::borrow::Cow<'a, Self>>>>,
153            ) -> $crate::SerializationResult<arrow::array::ArrayRef>
154            where
155                Self: 'a,
156            {
157                Err($crate::SerializationError::not_implemented(
158                    <Self as $crate::Component>::name(),
159                    "TUIDs are never nullable, use `to_arrow()` instead",
160                ))
161            }
162
163            #[inline]
164            fn to_arrow<'a>(
165                values: impl IntoIterator<Item = impl Into<std::borrow::Cow<'a, Self>>>,
166            ) -> $crate::SerializationResult<arrow::array::ArrayRef> {
167                let values = values.into_iter().map(|value| {
168                    let value: ::std::borrow::Cow<'a, Self> = value.into();
169                    value.into_owned()
170                });
171                <$crate::external::re_tuid::Tuid as $crate::Loggable>::to_arrow(
172                    values.into_iter().map(|$typ(tuid)| tuid),
173                )
174            }
175
176            #[inline]
177            fn from_arrow(
178                array: &dyn arrow::array::Array,
179            ) -> $crate::DeserializationResult<Vec<Self>> {
180                Ok(
181                    <$crate::external::re_tuid::Tuid as $crate::Loggable>::from_arrow(array)?
182                        .into_iter()
183                        .map(|tuid| Self(tuid))
184                        .collect(),
185                )
186            }
187        }
188
189        impl $crate::Component for $typ {
190            #[inline]
191            fn name() -> $crate::ComponentName {
192                $fqname.into()
193            }
194
195            #[inline]
196            fn descriptor() -> $crate::ComponentDescriptor {
197                $crate::ComponentDescriptor::new($fqname)
198            }
199        }
200    };
201}