#![doc = document_features::document_features!()]
pub mod arrow_msg;
mod entry_id;
mod entry_name;
pub mod example_components;
pub mod hash;
mod index;
pub mod path;
mod instance;
mod vec_deque_ext;
use std::sync::Arc;
use arrow::array::RecordBatch as ArrowRecordBatch;
use re_build_info::CrateVersion;
use re_byte_size::SizeBytes;
pub use re_types_core::TimelineName;
pub use self::arrow_msg::{ArrowMsg, ArrowRecordBatchReleaseCallback};
pub use self::entry_id::{EntryId, EntryIdOrName};
pub use self::entry_name::{EntryName, InvalidEntryNameError};
pub use self::index::{
AbsoluteTimeRange, AbsoluteTimeRangeF, Duration, NonMinI64, TimeCell, TimeInt, TimePoint,
TimeReal, TimeType, Timeline, TimelinePoint, Timestamp, TimestampFormat, TimestampFormatKind,
TryFromIntError,
};
pub use self::instance::Instance;
pub use self::path::*;
pub use self::vec_deque_ext::{VecDequeInsertionExt, VecDequeRemovalExt, VecDequeSortingExt};
pub mod external {
pub use {arrow, re_tuid, re_types_core};
}
#[macro_export]
macro_rules! impl_into_enum {
($from_ty: ty, $enum_name: ident, $to_enum_variant: ident) => {
impl From<$from_ty> for $enum_name {
#[inline]
fn from(value: $from_ty) -> Self {
Self::$to_enum_variant(value)
}
}
};
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum StoreKind {
Recording,
Blueprint,
}
impl std::fmt::Display for StoreKind {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Recording => "Recording".fmt(f),
Self::Blueprint => "Blueprint".fmt(f),
}
}
}
impl std::str::FromStr for StoreKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"recording" => Ok(Self::Recording),
"blueprint" => Ok(Self::Blueprint),
unknown => Err(format!("{unknown:?} is not a valid StoreKind")),
}
}
}
#[test]
fn store_kind_str_roundtrip() {
{
let kind = StoreKind::Recording;
assert_eq!(kind, kind.to_string().parse().unwrap());
}
{
let kind = StoreKind::Blueprint;
assert_eq!(kind, kind.to_string().parse().unwrap());
}
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct StoreId {
kind: StoreKind,
application_id: ApplicationId,
recording_id: RecordingId,
}
impl std::fmt::Debug for StoreId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("StoreId")
.field(&self.kind)
.field(&self.application_id.as_str())
.field(&self.recording_id.as_str())
.finish()
}
}
impl StoreId {
#[inline]
pub fn new(
kind: StoreKind,
application_id: impl Into<ApplicationId>,
recording_id: impl Into<RecordingId>,
) -> Self {
Self {
kind,
application_id: application_id.into(),
recording_id: recording_id.into(),
}
}
#[inline]
pub fn recording(
application_id: impl Into<ApplicationId>,
recording_id: impl Into<RecordingId>,
) -> Self {
Self::new(StoreKind::Recording, application_id, recording_id)
}
#[inline]
pub fn default_blueprint(application_id: ApplicationId) -> Self {
let recording_id = application_id.as_recording_id();
Self::new(StoreKind::Blueprint, application_id, recording_id)
}
#[inline]
pub fn random(kind: StoreKind, application_id: impl Into<ApplicationId>) -> Self {
Self {
kind,
recording_id: RecordingId::random(),
application_id: application_id.into(),
}
}
#[inline]
pub fn empty_recording() -> Self {
Self::new(StoreKind::Recording, "<EMPTY>", "<EMPTY>")
}
#[inline]
pub fn with_recording_id(self, recording_id: impl Into<RecordingId>) -> Self {
Self {
kind: self.kind,
recording_id: recording_id.into(),
application_id: self.application_id,
}
}
#[inline]
pub fn with_application_id(self, application_id: impl Into<ApplicationId>) -> Self {
Self {
kind: self.kind,
recording_id: self.recording_id,
application_id: application_id.into(),
}
}
#[inline]
pub fn from_uuid(kind: StoreKind, application_id: ApplicationId, uuid: uuid::Uuid) -> Self {
Self::new(kind, application_id, uuid.simple().to_string())
}
#[inline]
pub fn is_empty_recording(&self) -> bool {
self.kind == StoreKind::Recording && self.recording_id.as_str() == "<EMPTY>"
}
#[inline]
pub fn is_default_blueprint(&self) -> bool {
self.kind == StoreKind::Blueprint
&& self.application_id.as_str() == self.recording_id.as_str()
}
#[inline]
pub fn kind(&self) -> StoreKind {
self.kind
}
#[inline]
pub fn is_recording(&self) -> bool {
self.kind == StoreKind::Recording
}
#[inline]
pub fn is_blueprint(&self) -> bool {
self.kind == StoreKind::Blueprint
}
#[inline]
pub fn recording_id(&self) -> &RecordingId {
&self.recording_id
}
#[inline]
pub fn application_id(&self) -> &ApplicationId {
&self.application_id
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ApplicationId(Arc<String>);
impl From<&str> for ApplicationId {
fn from(s: &str) -> Self {
Self(Arc::new(s.to_owned()))
}
}
impl From<String> for ApplicationId {
fn from(s: String) -> Self {
Self(Arc::new(s))
}
}
impl SizeBytes for ApplicationId {
#[inline]
fn heap_size_bytes(&self) -> u64 {
self.0.heap_size_bytes()
}
}
impl ApplicationId {
pub fn unknown() -> Self {
static UNKNOWN_APP_ID: std::sync::LazyLock<ApplicationId> =
std::sync::LazyLock::new(|| ApplicationId(Arc::new("unknown_app_id".to_owned())));
UNKNOWN_APP_ID.clone()
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn random() -> Self {
Self(Arc::new(format!("app_{}", uuid::Uuid::new_v4().simple())))
}
fn as_recording_id(&self) -> RecordingId {
RecordingId(Arc::clone(&self.0))
}
}
impl std::fmt::Display for ApplicationId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct RecordingId(Arc<String>);
impl From<&str> for RecordingId {
fn from(s: &str) -> Self {
Self(Arc::new(s.to_owned()))
}
}
impl From<String> for RecordingId {
fn from(s: String) -> Self {
Self(Arc::new(s))
}
}
impl SizeBytes for RecordingId {
#[inline]
fn heap_size_bytes(&self) -> u64 {
self.0.heap_size_bytes()
}
}
impl RecordingId {
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn random() -> Self {
Self(Arc::new(format!("rec_{}", uuid::Uuid::new_v4().simple())))
}
}
impl std::fmt::Display for RecordingId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableId(Arc<String>);
impl TableId {
pub fn new(id: String) -> Self {
Self(Arc::new(id))
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl From<&str> for TableId {
fn from(s: &str) -> Self {
Self(Arc::new(s.into()))
}
}
impl From<String> for TableId {
fn from(s: String) -> Self {
Self(Arc::new(s))
}
}
impl std::fmt::Display for TableId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, Debug, PartialEq, Eq)] pub struct BlueprintActivationCommand {
pub blueprint_id: StoreId,
pub make_active: bool,
pub make_default: bool,
}
impl BlueprintActivationCommand {
pub fn make_default(blueprint_id: StoreId) -> Self {
Self {
blueprint_id,
make_active: false,
make_default: true,
}
}
pub fn make_active(blueprint_id: StoreId) -> Self {
Self {
blueprint_id,
make_active: true,
make_default: true,
}
}
}
#[must_use]
#[derive(Clone, Debug, PartialEq)] pub enum LogMsg {
SetStoreInfo(SetStoreInfo),
ArrowMsg(StoreId, ArrowMsg),
BlueprintActivationCommand(BlueprintActivationCommand),
}
impl LogMsg {
pub fn store_id(&self) -> &StoreId {
match self {
Self::SetStoreInfo(msg) => &msg.info.store_id,
Self::ArrowMsg(store_id, _) => store_id,
Self::BlueprintActivationCommand(cmd) => &cmd.blueprint_id,
}
}
pub fn set_store_id(&mut self, new_store_id: StoreId) {
match self {
Self::SetStoreInfo(store_info) => {
store_info.info.store_id = new_store_id;
}
Self::ArrowMsg(store_id, _) => {
*store_id = new_store_id;
}
Self::BlueprintActivationCommand(cmd) => {
cmd.blueprint_id = new_store_id;
}
}
}
pub fn arrow_record_batch_mut(&mut self) -> Option<&mut ArrowRecordBatch> {
match self {
Self::ArrowMsg(_, arrow_msg) => Some(&mut arrow_msg.batch),
_ => None,
}
}
pub fn insert_arrow_record_batch_metadata(&mut self, key: String, value: String) {
if let Some(record_batch) = self.arrow_record_batch_mut() {
record_batch.schema_metadata_mut().insert(key, value);
}
}
}
impl_into_enum!(SetStoreInfo, LogMsg, SetStoreInfo);
impl_into_enum!(
BlueprintActivationCommand,
LogMsg,
BlueprintActivationCommand
);
#[must_use]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SetStoreInfo {
pub row_id: re_tuid::Tuid,
pub info: StoreInfo,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StoreInfo {
pub store_id: StoreId,
pub cloned_from: Option<StoreId>,
pub store_source: StoreSource,
pub store_version: Option<CrateVersion>,
}
impl StoreInfo {
pub fn new(store_id: StoreId, store_source: StoreSource) -> Self {
Self {
store_id,
cloned_from: None,
store_source,
store_version: Some(CrateVersion::LOCAL),
}
}
pub fn new_unversioned(store_id: StoreId, store_source: StoreSource) -> Self {
Self {
store_id,
cloned_from: None,
store_source,
store_version: None,
}
}
pub fn testing() -> Self {
Self::new_unversioned(
StoreId::random(StoreKind::Recording, "test_app"),
StoreSource::Other("test".to_owned()),
)
}
pub fn testing_with_recording_id(recording_id: impl Into<RecordingId>) -> Self {
Self::new_unversioned(
StoreId::new(StoreKind::Recording, "test_app", recording_id),
StoreSource::Other("test".to_owned()),
)
}
}
impl StoreInfo {
pub fn is_app_default_blueprint(&self) -> bool {
self.application_id().as_str() == self.recording_id().as_str()
}
pub fn application_id(&self) -> &ApplicationId {
self.store_id.application_id()
}
pub fn recording_id(&self) -> &RecordingId {
self.store_id.recording_id()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct PythonVersion {
pub major: u8,
pub minor: u8,
pub patch: u8,
pub suffix: String,
}
impl std::fmt::Debug for PythonVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::fmt::Display for PythonVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self {
major,
minor,
patch,
suffix,
} = self;
write!(f, "{major}.{minor}.{patch}{suffix}")
}
}
impl std::str::FromStr for PythonVersion {
type Err = PythonVersionParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(PythonVersionParseError::MissingMajor);
}
let (major, rest) = s
.split_once('.')
.ok_or(PythonVersionParseError::MissingMinor)?;
if rest.is_empty() {
return Err(PythonVersionParseError::MissingMinor);
}
let (minor, rest) = rest
.split_once('.')
.ok_or(PythonVersionParseError::MissingPatch)?;
if rest.is_empty() {
return Err(PythonVersionParseError::MissingPatch);
}
let pos = rest.bytes().position(|v| !v.is_ascii_digit());
let (patch, suffix) = match pos {
Some(pos) => rest.split_at(pos),
None => (rest, ""),
};
Ok(Self {
major: major
.parse()
.map_err(PythonVersionParseError::InvalidMajor)?,
minor: minor
.parse()
.map_err(PythonVersionParseError::InvalidMinor)?,
patch: patch
.parse()
.map_err(PythonVersionParseError::InvalidPatch)?,
suffix: suffix.into(),
})
}
}
#[derive(Debug, thiserror::Error)]
pub enum PythonVersionParseError {
#[error("missing major version")]
MissingMajor,
#[error("missing minor version")]
MissingMinor,
#[error("missing patch version")]
MissingPatch,
#[error("invalid major version: {0}")]
InvalidMajor(std::num::ParseIntError),
#[error("invalid minor version: {0}")]
InvalidMinor(std::num::ParseIntError),
#[error("invalid patch version: {0}")]
InvalidPatch(std::num::ParseIntError),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum FileSource {
Cli,
Uri,
DragAndDrop {
recommended_store_id: Option<StoreId>,
force_store_info: bool,
},
FileDialog {
recommended_store_id: Option<StoreId>,
force_store_info: bool,
},
Sdk,
}
impl FileSource {
pub fn recommended_store_id(&self) -> Option<&StoreId> {
match self {
Self::FileDialog {
recommended_store_id,
..
}
| Self::DragAndDrop {
recommended_store_id,
..
} => recommended_store_id.as_ref(),
Self::Cli | Self::Uri | Self::Sdk => None,
}
}
#[inline]
pub fn force_store_info(&self) -> bool {
match self {
Self::FileDialog {
force_store_info, ..
}
| Self::DragAndDrop {
force_store_info, ..
} => *force_store_info,
Self::Cli | Self::Uri | Self::Sdk => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StoreSource {
Unknown,
CSdk,
PythonSdk(PythonVersion),
RustSdk {
rustc_version: String,
llvm_version: String,
},
File {
file_source: FileSource,
},
Viewer,
Other(String),
}
impl std::fmt::Display for StoreSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => "Unknown".fmt(f),
Self::CSdk => "C SDK".fmt(f),
Self::PythonSdk(version) => write!(f, "Python {version} SDK"),
Self::RustSdk { rustc_version, .. } => write!(f, "Rust SDK (rustc {rustc_version})"),
Self::File { file_source, .. } => match file_source {
FileSource::Cli => write!(f, "File via CLI"),
FileSource::Uri => write!(f, "File via URI"),
FileSource::DragAndDrop { .. } => write!(f, "File via drag-and-drop"),
FileSource::FileDialog { .. } => write!(f, "File via file dialog"),
FileSource::Sdk => write!(f, "File via SDK"),
},
Self::Viewer => write!(f, "Viewer-generated"),
Self::Other(string) => format!("{string:?}").fmt(f), }
}
}
#[must_use]
#[derive(Clone, Debug, PartialEq)]
pub struct TableMsg {
pub id: TableId,
pub data: ArrowRecordBatch,
}
impl re_byte_size::SizeBytes for TableMsg {
#[inline]
fn heap_size_bytes(&self) -> u64 {
let Self { id: _, data } = self;
data.heap_size_bytes()
}
}
impl TableMsg {
pub fn insert_arrow_record_batch_metadata(&mut self, key: String, value: String) {
self.data.schema_metadata_mut().insert(key, value);
}
}
#[inline]
pub fn build_log_time(log_time: Timestamp) -> (Timeline, TimeInt) {
(
Timeline::log_time(),
TimeInt::new_temporal(log_time.nanos_since_epoch()),
)
}
#[inline]
pub fn build_frame_nr(frame_nr: impl TryInto<TimeInt>) -> (Timeline, TimeInt) {
(
Timeline::new("frame_nr", TimeType::Sequence),
TimeInt::saturated_temporal(frame_nr),
)
}
#[inline]
pub fn build_index_value(value: impl TryInto<TimeInt>, time_type: TimeType) -> (Timeline, TimeInt) {
let timeline_name = match time_type {
TimeType::Sequence => "frame_nr",
TimeType::DurationNs => "duration",
TimeType::TimestampNs => "timestamp",
};
(
Timeline::new(timeline_name, time_type),
TimeInt::saturated_temporal(value),
)
}
impl SizeBytes for StoreId {
#[inline]
fn heap_size_bytes(&self) -> u64 {
let Self {
kind: _,
recording_id: id,
application_id,
} = self;
id.heap_size_bytes() + application_id.heap_size_bytes()
}
}
impl SizeBytes for PythonVersion {
#[inline]
fn heap_size_bytes(&self) -> u64 {
let Self {
major: _,
minor: _,
patch: _,
suffix,
} = self;
suffix.heap_size_bytes()
}
}
impl SizeBytes for FileSource {
#[inline]
fn heap_size_bytes(&self) -> u64 {
match self {
Self::Uri | Self::Sdk | Self::Cli => 0,
Self::DragAndDrop {
recommended_store_id,
force_store_info,
}
| Self::FileDialog {
recommended_store_id,
force_store_info,
} => recommended_store_id.heap_size_bytes() + force_store_info.heap_size_bytes(),
}
}
}
impl SizeBytes for StoreSource {
#[inline]
fn heap_size_bytes(&self) -> u64 {
match self {
Self::Unknown | Self::CSdk | Self::Viewer => 0,
Self::PythonSdk(python_version) => python_version.heap_size_bytes(),
Self::RustSdk {
rustc_version,
llvm_version,
} => rustc_version.heap_size_bytes() + llvm_version.heap_size_bytes(),
Self::File { file_source } => file_source.heap_size_bytes(),
Self::Other(description) => description.heap_size_bytes(),
}
}
}
impl SizeBytes for StoreInfo {
#[inline]
fn heap_size_bytes(&self) -> u64 {
let Self {
store_id,
cloned_from: _,
store_source,
store_version,
} = self;
store_id.heap_size_bytes()
+ store_source.heap_size_bytes()
+ store_version.heap_size_bytes()
}
}
impl SizeBytes for SetStoreInfo {
#[inline]
fn heap_size_bytes(&self) -> u64 {
let Self { row_id, info } = self;
row_id.heap_size_bytes() + info.heap_size_bytes()
}
}
impl SizeBytes for BlueprintActivationCommand {
#[inline]
fn heap_size_bytes(&self) -> u64 {
0
}
}
impl SizeBytes for ArrowMsg {
#[inline]
fn heap_size_bytes(&self) -> u64 {
let Self {
chunk_id,
batch,
on_release: _,
} = self;
chunk_id.heap_size_bytes() + batch.heap_size_bytes()
}
}
impl SizeBytes for LogMsg {
#[inline]
fn heap_size_bytes(&self) -> u64 {
match self {
Self::SetStoreInfo(set_store_info) => set_store_info.heap_size_bytes(),
Self::ArrowMsg(store_id, arrow_msg) => {
store_id.heap_size_bytes() + arrow_msg.heap_size_bytes()
}
Self::BlueprintActivationCommand(blueprint_activation_command) => {
blueprint_activation_command.heap_size_bytes()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_python_version() {
macro_rules! assert_parse_err {
($input:literal, $expected:pat) => {
let actual = $input.parse::<PythonVersion>();
assert!(
matches!(actual, Err($expected)),
"actual: {actual:?}, expected: {}",
stringify!($expected)
);
};
}
macro_rules! assert_parse_ok {
($input:literal, $expected:expr) => {
let actual = $input.parse::<PythonVersion>().expect("failed to parse");
assert_eq!(actual, $expected);
};
}
assert_parse_err!("", PythonVersionParseError::MissingMajor);
assert_parse_err!("3", PythonVersionParseError::MissingMinor);
assert_parse_err!("3.", PythonVersionParseError::MissingMinor);
assert_parse_err!("3.11", PythonVersionParseError::MissingPatch);
assert_parse_err!("3.11.", PythonVersionParseError::MissingPatch);
assert_parse_err!("a.11.0", PythonVersionParseError::InvalidMajor(_));
assert_parse_err!("3.b.0", PythonVersionParseError::InvalidMinor(_));
assert_parse_err!("3.11.c", PythonVersionParseError::InvalidPatch(_));
assert_parse_ok!(
"3.11.0",
PythonVersion {
major: 3,
minor: 11,
patch: 0,
suffix: String::new(),
}
);
assert_parse_ok!(
"3.11.0a1",
PythonVersion {
major: 3,
minor: 11,
patch: 0,
suffix: "a1".to_owned(),
}
);
}
}