Skip to main content

re_protos/v1alpha1/
rerun.common.v1alpha1.ext.rs

1use std::hash::Hasher as _;
2
3use arrow::datatypes::Schema as ArrowSchema;
4use arrow::error::ArrowError;
5
6use re_log_types::external::re_types_core::ComponentDescriptor;
7use re_log_types::{RecordingId, StoreKind, TableId};
8
9use crate::{TypeConversionError, invalid_field, missing_field};
10
11/// Compression format used.
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13#[repr(u8)]
14pub enum Compression {
15    Off = 0,
16
17    /// Very fast compression and decompression, but not very good compression ratio.
18    LZ4 = 1,
19}
20
21impl From<crate::common::v1alpha1::Compression> for Compression {
22    fn from(value: crate::common::v1alpha1::Compression) -> Self {
23        match value {
24            crate::common::v1alpha1::Compression::Unspecified
25            | crate::common::v1alpha1::Compression::None => Self::Off,
26            crate::common::v1alpha1::Compression::Lz4 => Self::LZ4,
27        }
28    }
29}
30
31impl From<Compression> for crate::common::v1alpha1::Compression {
32    fn from(value: Compression) -> Self {
33        match value {
34            Compression::Off => Self::None,
35            Compression::LZ4 => Self::Lz4,
36        }
37    }
38}
39
40// --- Arrow ---
41
42impl TryFrom<&crate::common::v1alpha1::Schema> for ArrowSchema {
43    type Error = ArrowError;
44
45    fn try_from(value: &crate::common::v1alpha1::Schema) -> Result<Self, Self::Error> {
46        let schema_bytes = value
47            .arrow_schema
48            .as_ref()
49            .ok_or(ArrowError::InvalidArgumentError(
50                "missing schema bytes".to_owned(),
51            ))?;
52        Ok(Self::clone(
53            re_sorbet::migrated_schema_from_ipc(schema_bytes)?.as_ref(),
54        ))
55    }
56}
57
58impl TryFrom<&ArrowSchema> for crate::common::v1alpha1::Schema {
59    type Error = ArrowError;
60
61    fn try_from(value: &ArrowSchema) -> Result<Self, Self::Error> {
62        Ok(Self {
63            arrow_schema: Some(re_sorbet::ipc_from_schema(value)?.into()),
64        })
65    }
66}
67
68impl TryFrom<crate::common::v1alpha1::Schema> for ArrowSchema {
69    type Error = ArrowError;
70
71    fn try_from(value: crate::common::v1alpha1::Schema) -> Result<Self, Self::Error> {
72        (&value).try_into()
73    }
74}
75
76// --- EntryId ---
77
78impl From<re_log_types::EntryId> for crate::common::v1alpha1::EntryId {
79    #[inline]
80    fn from(value: re_log_types::EntryId) -> Self {
81        Self {
82            id: Some(value.id.into()),
83        }
84    }
85}
86
87impl TryFrom<crate::common::v1alpha1::EntryId> for re_log_types::EntryId {
88    type Error = TypeConversionError;
89
90    fn try_from(value: crate::common::v1alpha1::EntryId) -> Result<Self, Self::Error> {
91        let id = value
92            .id
93            .ok_or(missing_field!(crate::common::v1alpha1::EntryId, "id"))?;
94        Ok(Self { id: id.try_into()? })
95    }
96}
97
98// shortcuts
99
100impl From<re_tuid::Tuid> for crate::common::v1alpha1::EntryId {
101    fn from(id: re_tuid::Tuid) -> Self {
102        let id: re_log_types::EntryId = id.into();
103        Self {
104            id: Some(id.id.into()),
105        }
106    }
107}
108
109impl TryFrom<crate::common::v1alpha1::Tuid> for crate::common::v1alpha1::EntryId {
110    type Error = TypeConversionError;
111
112    fn try_from(id: crate::common::v1alpha1::Tuid) -> Result<Self, Self::Error> {
113        let id: re_tuid::Tuid = id.try_into()?;
114        Ok(Self {
115            id: Some(id.into()),
116        })
117    }
118}
119
120// --- SegmentId ---
121
122#[derive(
123    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
124)]
125pub struct SegmentId {
126    pub id: String,
127}
128
129impl SegmentId {
130    #[inline]
131    pub fn new(id: String) -> Self {
132        Self { id }
133    }
134}
135
136impl From<String> for SegmentId {
137    fn from(id: String) -> Self {
138        Self { id }
139    }
140}
141
142impl From<&str> for SegmentId {
143    fn from(id: &str) -> Self {
144        Self { id: id.to_owned() }
145    }
146}
147
148impl std::fmt::Display for SegmentId {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        self.id.fmt(f)
151    }
152}
153
154impl TryFrom<crate::common::v1alpha1::SegmentId> for SegmentId {
155    type Error = TypeConversionError;
156
157    fn try_from(value: crate::common::v1alpha1::SegmentId) -> Result<Self, Self::Error> {
158        Ok(Self {
159            id: value
160                .id
161                .ok_or(missing_field!(crate::common::v1alpha1::SegmentId, "id"))?,
162        })
163    }
164}
165
166impl From<SegmentId> for crate::common::v1alpha1::SegmentId {
167    fn from(value: SegmentId) -> Self {
168        Self { id: Some(value.id) }
169    }
170}
171
172impl AsRef<str> for SegmentId {
173    fn as_ref(&self) -> &str {
174        self.id.as_str()
175    }
176}
177
178// shortcuts
179
180impl From<String> for crate::common::v1alpha1::SegmentId {
181    fn from(id: String) -> Self {
182        Self { id: Some(id) }
183    }
184}
185
186impl From<&str> for crate::common::v1alpha1::SegmentId {
187    fn from(id: &str) -> Self {
188        Self {
189            id: Some(id.to_owned()),
190        }
191    }
192}
193
194// --- DatasetHandle ---
195
196#[derive(Debug, Clone)]
197pub struct DatasetHandle {
198    pub id: Option<re_log_types::EntryId>,
199    pub store_kind: StoreKind,
200    pub url: url::Url,
201}
202
203impl DatasetHandle {
204    /// Create a new dataset handle
205    pub fn new(url: url::Url, store_kind: StoreKind) -> Self {
206        Self {
207            id: None,
208            store_kind,
209            url,
210        }
211    }
212}
213
214impl TryFrom<crate::common::v1alpha1::DatasetHandle> for DatasetHandle {
215    type Error = TypeConversionError;
216
217    fn try_from(value: crate::common::v1alpha1::DatasetHandle) -> Result<Self, Self::Error> {
218        Ok(Self {
219            id: value.entry_id.map(|id| id.try_into()).transpose()?,
220            store_kind: crate::common::v1alpha1::StoreKind::try_from(value.store_kind)?.into(),
221            url: value
222                .dataset_url
223                .ok_or(missing_field!(
224                    crate::common::v1alpha1::DatasetHandle,
225                    "dataset_url"
226                ))?
227                .parse()
228                .map_err(|err| {
229                    invalid_field!(crate::common::v1alpha1::DatasetHandle, "dataset_url", err)
230                })?,
231        })
232    }
233}
234
235impl From<DatasetHandle> for crate::common::v1alpha1::DatasetHandle {
236    fn from(value: DatasetHandle) -> Self {
237        Self {
238            entry_id: value.id.map(Into::into),
239            store_kind: crate::common::v1alpha1::StoreKind::from(value.store_kind) as i32,
240            dataset_url: Some(value.url.to_string()),
241        }
242    }
243}
244
245// --- TaskId ---
246
247//TODO(ab): we should migrate the full `TaskId` wrapper from `redap_tasks`
248impl crate::common::v1alpha1::TaskId {
249    pub fn new() -> Self {
250        Self::from_hashable(re_tuid::Tuid::new())
251    }
252
253    pub fn from_hashable<H: std::hash::Hash>(hashable: H) -> Self {
254        let mut hasher = std::hash::DefaultHasher::new();
255        hashable.hash(&mut hasher);
256        let id = hasher.finish();
257
258        Self {
259            id: format!("task_{id:016x}"),
260        }
261    }
262}
263
264// ---
265
266impl TryFrom<crate::common::v1alpha1::Tuid> for re_tuid::Tuid {
267    type Error = TypeConversionError;
268
269    fn try_from(value: crate::common::v1alpha1::Tuid) -> Result<Self, Self::Error> {
270        let time_ns = value
271            .time_ns
272            .ok_or(missing_field!(crate::common::v1alpha1::Tuid, "time_ns"))?;
273        let inc = value
274            .inc
275            .ok_or(missing_field!(crate::common::v1alpha1::Tuid, "inc"))?;
276
277        Ok(Self::from_nanos_and_inc(time_ns, inc))
278    }
279}
280
281impl From<re_tuid::Tuid> for crate::common::v1alpha1::Tuid {
282    fn from(value: re_tuid::Tuid) -> Self {
283        Self {
284            time_ns: Some(value.nanos_since_epoch()),
285            inc: Some(value.inc()),
286        }
287    }
288}
289
290impl From<re_log_types::EntityPath> for crate::common::v1alpha1::EntityPath {
291    fn from(value: re_log_types::EntityPath) -> Self {
292        Self {
293            path: value.to_string(),
294        }
295    }
296}
297
298impl TryFrom<crate::common::v1alpha1::EntityPath> for re_log_types::EntityPath {
299    type Error = TypeConversionError;
300
301    fn try_from(value: crate::common::v1alpha1::EntityPath) -> Result<Self, Self::Error> {
302        Self::parse_strict(&value.path)
303            .map_err(|err| invalid_field!(crate::common::v1alpha1::EntityPath, "path", err))
304    }
305}
306
307impl From<re_log_types::AbsoluteTimeRange> for crate::common::v1alpha1::TimeRange {
308    fn from(value: re_log_types::AbsoluteTimeRange) -> Self {
309        Self {
310            start: value.min().as_i64(),
311            end: value.max().as_i64(),
312        }
313    }
314}
315
316impl From<crate::common::v1alpha1::TimeRange> for re_log_types::AbsoluteTimeRange {
317    fn from(value: crate::common::v1alpha1::TimeRange) -> Self {
318        Self::new(
319            re_log_types::TimeInt::new_temporal(value.start),
320            re_log_types::TimeInt::new_temporal(value.end),
321        )
322    }
323}
324
325impl From<re_log_types::AbsoluteTimeRange> for crate::common::v1alpha1::IndexRange {
326    fn from(value: re_log_types::AbsoluteTimeRange) -> Self {
327        Self {
328            time_range: Some(value.into()),
329        }
330    }
331}
332
333impl TryFrom<crate::common::v1alpha1::IndexRange> for re_log_types::AbsoluteTimeRange {
334    type Error = TypeConversionError;
335
336    fn try_from(value: crate::common::v1alpha1::IndexRange) -> Result<Self, Self::Error> {
337        value
338            .time_range
339            .ok_or(missing_field!(
340                crate::common::v1alpha1::IndexRange,
341                "time_range"
342            ))
343            .map(|time_range| Self::new(time_range.start, time_range.end))
344    }
345}
346
347impl From<crate::common::v1alpha1::Timeline> for re_log_types::TimelineName {
348    fn from(value: crate::common::v1alpha1::Timeline) -> Self {
349        Self::new(&value.name)
350    }
351}
352
353impl From<re_log_types::TimelineName> for crate::common::v1alpha1::Timeline {
354    fn from(value: re_log_types::TimelineName) -> Self {
355        Self {
356            name: value.to_string(),
357        }
358    }
359}
360
361impl From<re_log_types::Timeline> for crate::common::v1alpha1::Timeline {
362    fn from(value: re_log_types::Timeline) -> Self {
363        Self {
364            name: value.name().to_string(),
365        }
366    }
367}
368
369impl TryFrom<crate::common::v1alpha1::IndexColumnSelector> for re_log_types::TimelineName {
370    type Error = TypeConversionError;
371
372    fn try_from(value: crate::common::v1alpha1::IndexColumnSelector) -> Result<Self, Self::Error> {
373        let timeline = value.timeline.ok_or(missing_field!(
374            crate::common::v1alpha1::IndexColumnSelector,
375            "timeline"
376        ))?;
377        Ok(timeline.into())
378    }
379}
380
381impl From<re_log_types::TimelineName> for crate::common::v1alpha1::IndexColumnSelector {
382    fn from(value: re_log_types::TimelineName) -> Self {
383        Self {
384            timeline: Some(value.into()),
385        }
386    }
387}
388
389impl From<crate::common::v1alpha1::ApplicationId> for re_log_types::ApplicationId {
390    #[inline]
391    fn from(value: crate::common::v1alpha1::ApplicationId) -> Self {
392        Self::from(value.id)
393    }
394}
395
396impl From<re_log_types::ApplicationId> for crate::common::v1alpha1::ApplicationId {
397    #[inline]
398    fn from(value: re_log_types::ApplicationId) -> Self {
399        Self {
400            id: value.to_string(),
401        }
402    }
403}
404
405impl From<crate::common::v1alpha1::StoreKind> for re_log_types::StoreKind {
406    #[inline]
407    fn from(value: crate::common::v1alpha1::StoreKind) -> Self {
408        match value {
409            crate::common::v1alpha1::StoreKind::Unspecified
410            | crate::common::v1alpha1::StoreKind::Recording => Self::Recording,
411            crate::common::v1alpha1::StoreKind::Blueprint => Self::Blueprint,
412        }
413    }
414}
415
416impl From<re_log_types::StoreKind> for crate::common::v1alpha1::StoreKind {
417    #[inline]
418    fn from(value: re_log_types::StoreKind) -> Self {
419        match value {
420            re_log_types::StoreKind::Recording => Self::Recording,
421            re_log_types::StoreKind::Blueprint => Self::Blueprint,
422        }
423    }
424}
425
426/// The store id failed to deserialize due to missing application id.
427///
428/// This may happen when migrating older RRD (before application ID was moved to `StoreId`). This
429/// error can be recovered from if the application ID is known.
430//TODO(#10730): this is specifically for 0.24 back compat. Switch to `TypeConversionError` when cleaning up.
431#[derive(Debug, Clone)]
432pub struct StoreIdMissingApplicationIdError {
433    pub store_kind: re_log_types::StoreKind,
434    pub recording_id: RecordingId,
435}
436
437impl StoreIdMissingApplicationIdError {
438    /// Recover the store ID by providing the application ID.
439    pub fn recover(self, application_id: re_log_types::ApplicationId) -> re_log_types::StoreId {
440        re_log_types::StoreId::new(self.store_kind, application_id, self.recording_id)
441    }
442
443    #[inline]
444    pub fn into_type_conversion_error(self, msg: impl Into<String>) -> TypeConversionError {
445        TypeConversionError::LegacyStoreIdError(format!(
446            "{} (kind: {}, recording_id: {})",
447            msg.into(),
448            self.store_kind,
449            self.recording_id
450        ))
451    }
452}
453
454/// Convert a store id
455impl TryFrom<crate::common::v1alpha1::StoreId> for re_log_types::StoreId {
456    type Error = StoreIdMissingApplicationIdError;
457
458    #[inline]
459    fn try_from(value: crate::common::v1alpha1::StoreId) -> Result<Self, Self::Error> {
460        let store_kind = value.kind().into();
461        let recording_id = RecordingId::from(value.recording_id);
462
463        //TODO(#10730): switch to `TypeConversionError` when cleaning up 0.24 back compat
464        match value.application_id {
465            None => Err(StoreIdMissingApplicationIdError {
466                store_kind,
467                recording_id,
468            }),
469            Some(application_id) => Ok(re_log_types::StoreId::new(
470                store_kind,
471                application_id,
472                recording_id,
473            )),
474        }
475    }
476}
477
478impl From<re_log_types::StoreId> for crate::common::v1alpha1::StoreId {
479    #[inline]
480    fn from(value: re_log_types::StoreId) -> Self {
481        let kind: crate::common::v1alpha1::StoreKind = value.kind().into();
482        Self {
483            kind: kind as i32,
484            recording_id: value.recording_id().as_str().to_owned(),
485            application_id: Some(value.application_id().clone().into()),
486        }
487    }
488}
489
490impl From<re_log_types::TableId> for crate::common::v1alpha1::TableId {
491    #[inline]
492    fn from(value: re_log_types::TableId) -> Self {
493        Self {
494            id: value.as_str().to_owned(),
495        }
496    }
497}
498
499impl From<crate::common::v1alpha1::TableId> for re_log_types::TableId {
500    #[inline]
501    fn from(value: crate::common::v1alpha1::TableId) -> Self {
502        TableId::from(value.id)
503    }
504}
505
506// --- Scanning & Querying ---
507
508#[derive(Debug, Default, Clone)]
509pub struct ScanParameters {
510    pub columns: Vec<String>,
511    pub on_missing_columns: IfMissingBehavior,
512    pub filter: Option<String>,
513    pub limit_offset: Option<i64>,
514    pub limit_len: Option<i64>,
515    pub order_by: Vec<ScanParametersOrderClause>,
516    pub explain_plan: bool,
517    pub explain_filter: bool,
518}
519
520impl TryFrom<crate::common::v1alpha1::ScanParameters> for ScanParameters {
521    type Error = TypeConversionError;
522
523    fn try_from(value: crate::common::v1alpha1::ScanParameters) -> Result<Self, Self::Error> {
524        Ok(Self {
525            columns: value.columns,
526            on_missing_columns: crate::common::v1alpha1::IfMissingBehavior::try_from(
527                value.on_missing_columns,
528            )?
529            .into(),
530            filter: value.filter,
531            limit_offset: value.limit_offset,
532            limit_len: value.limit_len,
533            order_by: value
534                .order_by
535                .into_iter()
536                .map(|ob| ob.try_into())
537                .collect::<Result<Vec<_>, _>>()?,
538            explain_plan: value.explain_plan,
539            explain_filter: value.explain_filter,
540        })
541    }
542}
543
544impl From<ScanParameters> for crate::common::v1alpha1::ScanParameters {
545    fn from(value: ScanParameters) -> Self {
546        Self {
547            columns: value.columns,
548            on_missing_columns: crate::common::v1alpha1::IfMissingBehavior::from(
549                value.on_missing_columns,
550            ) as _,
551            filter: value.filter,
552            limit_offset: value.limit_offset,
553            limit_len: value.limit_len,
554            order_by: value.order_by.into_iter().map(|ob| ob.into()).collect(),
555            explain_plan: value.explain_plan,
556            explain_filter: value.explain_filter,
557        }
558    }
559}
560
561#[derive(Debug, Default, Clone)]
562pub struct ScanParametersOrderClause {
563    pub descending: bool,
564    pub nulls_last: bool,
565    pub column_name: String,
566}
567
568impl TryFrom<crate::common::v1alpha1::ScanParametersOrderClause> for ScanParametersOrderClause {
569    type Error = TypeConversionError;
570
571    fn try_from(
572        value: crate::common::v1alpha1::ScanParametersOrderClause,
573    ) -> Result<Self, Self::Error> {
574        Ok(Self {
575            descending: value.descending,
576            nulls_last: value.nulls_last,
577            column_name: value.column_name.ok_or(missing_field!(
578                crate::common::v1alpha1::ScanParametersOrderClause,
579                "column_name"
580            ))?,
581        })
582    }
583}
584
585impl From<ScanParametersOrderClause> for crate::common::v1alpha1::ScanParametersOrderClause {
586    fn from(value: ScanParametersOrderClause) -> Self {
587        Self {
588            descending: value.descending,
589            nulls_last: value.nulls_last,
590            column_name: Some(value.column_name),
591        }
592    }
593}
594
595#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
596pub enum IfMissingBehavior {
597    Skip,
598    Error,
599}
600
601impl Default for IfMissingBehavior {
602    fn default() -> Self {
603        Self::Skip
604    }
605}
606
607impl From<crate::common::v1alpha1::IfMissingBehavior> for IfMissingBehavior {
608    fn from(value: crate::common::v1alpha1::IfMissingBehavior) -> Self {
609        use crate::common::v1alpha1 as common;
610        match value {
611            common::IfMissingBehavior::Unspecified | common::IfMissingBehavior::Skip => Self::Skip,
612            common::IfMissingBehavior::Error => Self::Error,
613        }
614    }
615}
616
617impl From<IfMissingBehavior> for crate::common::v1alpha1::IfMissingBehavior {
618    fn from(value: IfMissingBehavior) -> Self {
619        match value {
620            IfMissingBehavior::Skip => Self::Skip,
621            IfMissingBehavior::Error => Self::Error,
622        }
623    }
624}
625
626#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
627pub enum IfDuplicateBehavior {
628    Overwrite,
629    Skip,
630    Error,
631}
632
633impl Default for IfDuplicateBehavior {
634    fn default() -> Self {
635        Self::Skip
636    }
637}
638
639impl TryFrom<i32> for IfDuplicateBehavior {
640    type Error = TypeConversionError;
641
642    fn try_from(value: i32) -> Result<Self, TypeConversionError> {
643        let proto_value = crate::common::v1alpha1::IfDuplicateBehavior::try_from(value)?;
644        Ok(Self::from(proto_value))
645    }
646}
647
648impl From<crate::common::v1alpha1::IfDuplicateBehavior> for IfDuplicateBehavior {
649    fn from(value: crate::common::v1alpha1::IfDuplicateBehavior) -> Self {
650        use crate::common::v1alpha1 as common;
651        match value {
652            common::IfDuplicateBehavior::Unspecified | common::IfDuplicateBehavior::Skip => {
653                Self::Skip
654            }
655            common::IfDuplicateBehavior::Overwrite => Self::Overwrite,
656            common::IfDuplicateBehavior::Error => Self::Error,
657        }
658    }
659}
660
661impl From<IfDuplicateBehavior> for crate::common::v1alpha1::IfDuplicateBehavior {
662    fn from(value: IfDuplicateBehavior) -> Self {
663        match value {
664            IfDuplicateBehavior::Overwrite => Self::Overwrite,
665            IfDuplicateBehavior::Skip => Self::Skip,
666            IfDuplicateBehavior::Error => Self::Error,
667        }
668    }
669}
670
671// ---
672
673impl From<ComponentDescriptor> for crate::common::v1alpha1::ComponentDescriptor {
674    fn from(value: ComponentDescriptor) -> Self {
675        Self {
676            archetype: value.archetype.map(|n| n.full_name().to_owned()),
677            component: Some(value.component.to_string()),
678            component_type: value.component_type.map(|c| c.full_name().to_owned()),
679        }
680    }
681}
682
683impl TryFrom<crate::common::v1alpha1::ComponentDescriptor> for ComponentDescriptor {
684    type Error = TypeConversionError;
685
686    fn try_from(value: crate::common::v1alpha1::ComponentDescriptor) -> Result<Self, Self::Error> {
687        let crate::common::v1alpha1::ComponentDescriptor {
688            archetype,
689            component,
690            component_type,
691        } = value;
692
693        let component = component.ok_or(missing_field!(
694            crate::common::v1alpha1::ComponentDescriptor,
695            "component"
696        ))?;
697
698        Ok(ComponentDescriptor {
699            archetype: archetype.map(Into::into),
700            component: component.into(),
701            component_type: component_type.map(Into::into),
702        })
703    }
704}
705
706// ---
707
708impl From<re_build_info::BuildInfo> for crate::common::v1alpha1::BuildInfo {
709    fn from(build_info: re_build_info::BuildInfo) -> Self {
710        Self {
711            crate_name: Some(build_info.crate_name.to_string()),
712            features: Some(build_info.features.to_string()),
713            version: Some(build_info.version.into()),
714            rustc_version: Some(build_info.rustc_version.to_string()),
715            llvm_version: Some(build_info.llvm_version.to_string()),
716            git_hash: Some(build_info.git_hash.to_string()),
717            git_branch: Some(build_info.git_branch.to_string()),
718            target_triple: Some(build_info.target_triple.to_string()),
719            build_time: Some(build_info.datetime.to_string()),
720            is_debug_build: Some(build_info.is_debug_build),
721        }
722    }
723}
724
725impl From<crate::common::v1alpha1::BuildInfo> for re_build_info::BuildInfo {
726    fn from(build_info: crate::common::v1alpha1::BuildInfo) -> Self {
727        Self {
728            crate_name: build_info.crate_name().to_owned().into(),
729            features: build_info.features().to_owned().into(),
730            version: build_info.version.clone().unwrap_or_default().into(),
731            rustc_version: build_info.rustc_version().to_owned().into(),
732            llvm_version: build_info.llvm_version().to_owned().into(),
733            git_hash: build_info.git_hash().to_owned().into(),
734            git_branch: build_info.git_branch().to_owned().into(),
735            is_in_rerun_workspace: false,
736            target_triple: build_info.target_triple().to_owned().into(),
737            datetime: build_info.build_time().to_owned().into(),
738            is_debug_build: build_info.is_debug_build(),
739        }
740    }
741}
742
743impl From<re_build_info::CrateVersion> for crate::common::v1alpha1::SemanticVersion {
744    fn from(version: re_build_info::CrateVersion) -> Self {
745        crate::common::v1alpha1::SemanticVersion {
746            major: Some(version.major.into()),
747            minor: Some(version.minor.into()),
748            patch: Some(version.patch.into()),
749            meta: version.meta.map(Into::into),
750        }
751    }
752}
753
754impl From<crate::common::v1alpha1::SemanticVersion> for re_build_info::CrateVersion {
755    fn from(version: crate::common::v1alpha1::SemanticVersion) -> Self {
756        Self {
757            major: version.major() as u8,
758            minor: version.minor() as u8,
759            patch: version.patch() as u8,
760            meta: version.meta.map(Into::into),
761        }
762    }
763}
764
765impl From<re_build_info::Meta> for crate::common::v1alpha1::semantic_version::Meta {
766    fn from(version_meta: re_build_info::Meta) -> Self {
767        match version_meta {
768            re_build_info::Meta::Rc(v) => Self::Rc(v.into()),
769
770            re_build_info::Meta::Alpha(v) => Self::Alpha(v.into()),
771
772            re_build_info::Meta::DevAlpha { alpha, commit } => {
773                Self::DevAlpha(crate::common::v1alpha1::DevAlpha {
774                    alpha: Some(alpha.into()),
775                    commit: commit.map(|s| String::from_utf8_lossy(s).to_string()),
776                })
777            }
778        }
779    }
780}
781
782impl From<crate::common::v1alpha1::semantic_version::Meta> for re_build_info::Meta {
783    fn from(version_meta: crate::common::v1alpha1::semantic_version::Meta) -> Self {
784        match version_meta {
785            crate::common::v1alpha1::semantic_version::Meta::Rc(v) => Self::Rc(v as _),
786
787            crate::common::v1alpha1::semantic_version::Meta::Alpha(v) => Self::Alpha(v as _),
788
789            crate::common::v1alpha1::semantic_version::Meta::DevAlpha(dev_alpha) => {
790                Self::DevAlpha {
791                    alpha: dev_alpha.alpha() as u8,
792                    // TODO(cmc): support this, but that means DevAlpha is not-const
793                    // anymore, which trigger a chain reaction of changes that I really
794                    // don't want to get in right now.
795                    commit: None,
796                }
797            }
798        }
799    }
800}
801
802// --- DataframePart / RerunChunk <-> RecordBatch ---
803
804impl TryFrom<crate::common::v1alpha1::RerunChunk> for arrow::array::RecordBatch {
805    type Error = TypeConversionError;
806
807    fn try_from(value: crate::common::v1alpha1::RerunChunk) -> Result<Self, Self::Error> {
808        Self::try_from(&value)
809    }
810}
811
812impl TryFrom<&crate::common::v1alpha1::RerunChunk> for arrow::array::RecordBatch {
813    type Error = TypeConversionError;
814
815    fn try_from(value: &crate::common::v1alpha1::RerunChunk) -> Result<Self, Self::Error> {
816        match value.encoder_version() {
817            crate::common::v1alpha1::EncoderVersion::Unspecified => {
818                return Err(missing_field!(
819                    crate::common::v1alpha1::RerunChunk,
820                    "encoder_version"
821                ));
822            }
823
824            crate::common::v1alpha1::EncoderVersion::V0 => {
825                let Some(bytes) = value.payload.as_ref() else {
826                    return Err(missing_field!(
827                        crate::common::v1alpha1::DataframePart,
828                        "payload"
829                    ));
830                };
831                let Some(batch) =
832                    record_batch_from_ipc_bytes(bytes, Compression::Off, bytes.len() as u64)?
833                else {
834                    return Err(invalid_field!(
835                        crate::common::v1alpha1::RerunChunk,
836                        "payload",
837                        "empty"
838                    ));
839                };
840                Ok(batch)
841            }
842        }
843    }
844}
845
846impl From<arrow::array::RecordBatch> for crate::common::v1alpha1::RerunChunk {
847    fn from(value: arrow::array::RecordBatch) -> Self {
848        Self::from(&value)
849    }
850}
851
852impl From<&arrow::array::RecordBatch> for crate::common::v1alpha1::RerunChunk {
853    fn from(value: &arrow::array::RecordBatch) -> Self {
854        let version = crate::common::v1alpha1::EncoderVersion::V0;
855        Self {
856            encoder_version: version as i32,
857            payload: Some(record_batch_to_ipc_bytes(value, Compression::Off).0.into()),
858        }
859    }
860}
861
862impl TryFrom<crate::common::v1alpha1::DataframePart> for arrow::array::RecordBatch {
863    type Error = TypeConversionError;
864
865    fn try_from(value: crate::common::v1alpha1::DataframePart) -> Result<Self, Self::Error> {
866        Self::try_from(&value)
867    }
868}
869
870impl TryFrom<&crate::common::v1alpha1::DataframePart> for arrow::array::RecordBatch {
871    type Error = TypeConversionError;
872
873    fn try_from(value: &crate::common::v1alpha1::DataframePart) -> Result<Self, Self::Error> {
874        match value.encoder_version() {
875            crate::common::v1alpha1::EncoderVersion::Unspecified => {
876                return Err(missing_field!(
877                    crate::common::v1alpha1::RerunChunk,
878                    "encoder_version"
879                ));
880            }
881
882            crate::common::v1alpha1::EncoderVersion::V0 => {
883                let Some(bytes) = value.payload.as_ref() else {
884                    return Err(missing_field!(
885                        crate::common::v1alpha1::DataframePart,
886                        "payload"
887                    ));
888                };
889
890                let compression =
891                    crate::common::v1alpha1::Compression::try_from(value.compression)?;
892                let compression = Compression::from(compression);
893
894                let Some(batch) =
895                    record_batch_from_ipc_bytes(bytes, compression, value.uncompressed_size)?
896                else {
897                    return Err(invalid_field!(
898                        crate::common::v1alpha1::RerunChunk,
899                        "payload",
900                        "empty"
901                    ));
902                };
903                Ok(batch)
904            }
905        }
906    }
907}
908
909impl From<arrow::array::RecordBatch> for crate::common::v1alpha1::DataframePart {
910    fn from(value: arrow::array::RecordBatch) -> Self {
911        Self::from(&value)
912    }
913}
914
915impl From<&arrow::array::RecordBatch> for crate::common::v1alpha1::DataframePart {
916    fn from(value: &arrow::array::RecordBatch) -> Self {
917        let version = crate::common::v1alpha1::EncoderVersion::V0;
918        let compression = crate::common::v1alpha1::Compression::Lz4;
919        let (payload, uncompressed_size) = record_batch_to_ipc_bytes(value, compression.into());
920        Self {
921            encoder_version: version as i32, // make sure `version` is using the transport type!!
922            payload: Some(payload.into()),
923            compression: compression as i32, // make sure `compression` is using the transport type!!
924            uncompressed_size,
925        }
926    }
927}
928
929/// `RecordBatch` to IPC bytes. No I/O, no failures.
930#[tracing::instrument(level = "debug", skip_all)]
931fn record_batch_to_ipc_bytes(
932    batch: &arrow::array::RecordBatch,
933    compression: Compression,
934) -> (Vec<u8>, u64) {
935    let schema = batch.schema_ref().as_ref();
936
937    let mut uncompressed = Vec::new();
938
939    {
940        let mut sw = {
941            let _span = tracing::trace_span!("schema").entered();
942            arrow::ipc::writer::StreamWriter::try_new(&mut uncompressed, schema)
943                .expect("encoding the schema of a valid RecordBatch as IPC bytes into a growable in-memory buffer cannot possibly fail")
944        };
945
946        {
947            let _span = tracing::trace_span!("data").entered();
948            sw.write(batch)
949                .expect("encoding the data of a valid RecordBatch as IPC bytes into a growable in-memory buffer cannot possibly fail");
950        }
951
952        sw.finish()
953            .expect("encoding a valid RecordBatch as IPC bytes into a growable in-memory buffer cannot possibly fail");
954    }
955
956    let uncompressed_size = uncompressed.len() as u64;
957
958    let data = match compression {
959        Compression::Off => uncompressed,
960        Compression::LZ4 => {
961            re_tracing::profile_scope!("lz4::compress");
962            let _span = tracing::trace_span!("lz4::compress").entered();
963            lz4_flex::block::compress(&uncompressed)
964        }
965    };
966
967    (data, uncompressed_size)
968}
969
970/// IPC bytes to `RecordBatch`. `Ok(None)` if there's no data.
971#[tracing::instrument(level = "debug", skip_all)]
972fn record_batch_from_ipc_bytes(
973    bytes: &[u8],
974    compression: Compression,
975    uncompressed_size: u64,
976) -> Result<Option<arrow::array::RecordBatch>, ArrowError> {
977    let mut uncompressed = Vec::new();
978    let bytes = match compression {
979        Compression::Off => bytes,
980        Compression::LZ4 => {
981            re_tracing::profile_scope!("LZ4-decompress");
982            let _span = tracing::trace_span!("lz4::decompress").entered();
983            uncompressed.resize(uncompressed_size as usize, 0);
984            lz4_flex::block::decompress_into(bytes, &mut uncompressed).map_err(|err| {
985                ArrowError::ParseError(format!("LZ4 decompression failure: {err:#}"))
986            })?;
987            uncompressed.as_slice()
988        }
989    };
990
991    let mut stream = {
992        let _span = tracing::trace_span!("schema").entered();
993        arrow::ipc::reader::StreamReader::try_new(bytes, None)?
994    };
995
996    let _span = tracing::trace_span!("data").entered();
997    stream.next().transpose()
998}
999
1000// ---
1001
1002#[cfg(test)]
1003mod tests {
1004
1005    #[test]
1006    fn entity_path_conversion() {
1007        let entity_path = re_log_types::EntityPath::parse_strict("a/b/c").unwrap();
1008        let proto_entity_path: crate::common::v1alpha1::EntityPath = entity_path.clone().into();
1009        let entity_path2: re_log_types::EntityPath = proto_entity_path.try_into().unwrap();
1010        assert_eq!(entity_path, entity_path2);
1011    }
1012
1013    #[test]
1014    fn time_range_conversion() {
1015        let time_range = re_log_types::AbsoluteTimeRange::new(
1016            re_log_types::TimeInt::new_temporal(123456789),
1017            re_log_types::TimeInt::new_temporal(987654321),
1018        );
1019        let proto_time_range: crate::common::v1alpha1::TimeRange = time_range.into();
1020        let time_range2: re_log_types::AbsoluteTimeRange = proto_time_range.into();
1021        assert_eq!(time_range, time_range2);
1022    }
1023
1024    #[test]
1025    fn index_range_conversion() {
1026        let time_range = re_log_types::AbsoluteTimeRange::new(
1027            re_log_types::TimeInt::new_temporal(123456789),
1028            re_log_types::TimeInt::new_temporal(987654321),
1029        );
1030        let proto_index_range: crate::common::v1alpha1::IndexRange = time_range.into();
1031        let time_range2: re_log_types::AbsoluteTimeRange = proto_index_range.try_into().unwrap();
1032        assert_eq!(time_range, time_range2);
1033    }
1034
1035    #[test]
1036    fn index_column_selector_conversion() {
1037        let timeline = re_log_types::TimelineName::log_time();
1038        let proto_index_column_selector: crate::common::v1alpha1::IndexColumnSelector =
1039            crate::common::v1alpha1::IndexColumnSelector {
1040                timeline: Some(timeline.into()),
1041            };
1042        let timeline2: re_log_types::TimelineName = proto_index_column_selector.try_into().unwrap();
1043        assert_eq!(timeline, timeline2);
1044    }
1045
1046    #[test]
1047    fn application_id_conversion() {
1048        let application_id = re_log_types::ApplicationId::from("test");
1049        let proto_application_id: crate::common::v1alpha1::ApplicationId =
1050            application_id.clone().into();
1051        let application_id2: re_log_types::ApplicationId = proto_application_id.into();
1052        assert_eq!(application_id, application_id2);
1053    }
1054
1055    #[test]
1056    fn store_kind_conversion() {
1057        let store_kind = re_log_types::StoreKind::Recording;
1058        let proto_store_kind: crate::common::v1alpha1::StoreKind = store_kind.into();
1059        let store_kind2: re_log_types::StoreKind = proto_store_kind.into();
1060        assert_eq!(store_kind, store_kind2);
1061    }
1062
1063    #[test]
1064    fn store_id_conversion() {
1065        let store_id = re_log_types::StoreId::new(
1066            re_log_types::StoreKind::Recording,
1067            "test_app_id",
1068            "test_recording",
1069        );
1070        let proto_store_id: crate::common::v1alpha1::StoreId = store_id.clone().into();
1071        let store_id2: re_log_types::StoreId = proto_store_id.try_into().unwrap();
1072        assert_eq!(store_id, store_id2);
1073    }
1074
1075    #[test]
1076    fn test_tuid_conversion() {
1077        let tuid = re_tuid::Tuid::new();
1078        let proto_tuid: crate::common::v1alpha1::Tuid = tuid.into();
1079        let tuid2: re_tuid::Tuid = proto_tuid.try_into().unwrap();
1080        assert_eq!(tuid, tuid2);
1081    }
1082}