1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
use crate::{ComponentDescriptor, DeserializationResult};
#[expect(unused_imports, clippy::unused_trait_names)] // used in docstrings
use crate::{Component, Loggable};
// ---
/// An archetype is a high-level construct that represents a set of [`Component`]s that usually
/// play well with each other (i.e. they compose nicely).
///
/// Internally, it is no different than a collection of components, but working at that higher
/// layer of abstraction opens opportunities for nicer APIs & tools that wouldn't be possible
/// otherwise.
///
/// E.g. consider the `crate::archetypes::Points3D` archetype, which represents the set of
/// components to consider when working with a 3D point cloud within Rerun.
pub trait Archetype {
/// The fully-qualified name of this archetype, e.g. `rerun.archetypes.Points2D`.
fn name() -> ArchetypeName;
/// Readable name for displaying in UI.
fn display_name() -> &'static str;
// ---
/// Returns all component descriptors that _must_ be provided by the user when constructing this archetype.
fn required_components() -> std::borrow::Cow<'static, [ComponentDescriptor]>;
/// Returns all component descriptors that _should_ be provided by the user when constructing this archetype.
#[inline]
fn recommended_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
// TODO(#10512): Maybe add the "marker" component back here?
std::borrow::Cow::Owned(vec![])
}
/// Returns all component descriptors that _may_ be provided by the user when constructing this archetype.
#[inline]
fn optional_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
std::borrow::Cow::Borrowed(&[])
}
/// Returns all component descriptors that must, should and may be provided by the user when constructing
/// this archetype.
///
/// The default implementation always does the right thing, at the cost of some runtime
/// allocations.
/// If you know all your component descriptors statically, you can override this method to get rid of the
/// extra allocations.
#[inline]
fn all_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
[
Self::required_components().into_owned(),
Self::recommended_components().into_owned(),
Self::optional_components().into_owned(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
.into()
}
/// Utility method based on [`Self::all_components`] to return all component identifiers.
#[inline]
fn all_component_identifiers() -> impl Iterator<Item = ComponentIdentifier> {
match Self::all_components() {
// Need to resolve the Cow to work around borrow checker not being able to take ownership of it otherwise.
std::borrow::Cow::Borrowed(components) => {
itertools::Either::Left(components.iter().map(|c| c.component))
}
std::borrow::Cow::Owned(components) => {
itertools::Either::Right(components.into_iter().map(|c| c.component))
}
}
}
// ---
/// Given an iterator of Arrow arrays and their respective field metadata, deserializes them
/// into this archetype.
///
/// Arrow arrays that are unknown to this [`Archetype`] will simply be ignored and a warning
/// logged to stderr.
#[inline]
fn from_arrow(
data: impl IntoIterator<Item = (arrow::datatypes::Field, ::arrow::array::ArrayRef)>,
) -> DeserializationResult<Self>
where
Self: Sized,
{
Self::from_arrow_components(
data.into_iter()
.map(|(field, array)| (ComponentDescriptor::from(field), array)),
)
}
/// Given an iterator of Arrow arrays and their respective [`ComponentDescriptor`]s, deserializes them
/// into this archetype.
///
/// Arrow arrays that are unknown to this [`Archetype`] will simply be ignored and a warning
/// logged to stderr.
#[inline]
fn from_arrow_components(
data: impl IntoIterator<Item = (ComponentDescriptor, ::arrow::array::ArrayRef)>,
) -> DeserializationResult<Self>
where
Self: Sized,
{
_ = data; // NOTE: do this here to avoid breaking users' autocomplete snippets
Err(crate::DeserializationError::NotImplemented {
fqname: Self::name().to_string(),
backtrace: Box::new(std::backtrace::Backtrace::capture()),
})
}
}
/// Indicates that the archetype has reflection data available for it.
pub trait ArchetypeReflectionMarker {}
// ---
re_string_interner::declare_new_type!(
/// The fully-qualified name of an [`Archetype`], e.g. `rerun.archetypes.Points3D`.
#[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
pub struct ArchetypeName;
);
impl ArchetypeName {
/// Runs some asserts in debug mode to make sure the name is not weird.
#[inline]
#[track_caller]
pub fn sanity_check(&self) {
let full_name = self.0.as_str();
debug_assert!(
!full_name.starts_with("rerun.archetypes.rerun.archetypes."),
"DEBUG ASSERT: Found archetype with full name {full_name:?}. Maybe some bad round-tripping?"
);
}
/// Returns the fully-qualified name, e.g. `rerun.archetypes.Points3D`.
///
/// This is the default `Display` implementation for [`ArchetypeName`].
#[inline]
pub fn full_name(&self) -> &'static str {
self.sanity_check();
self.0.as_str()
}
/// Returns the unqualified name, e.g. `Points3D`.
///
/// Used for most UI elements.
///
/// ```
/// # use re_types_core::ArchetypeName;
/// assert_eq!(ArchetypeName::from("rerun.archetypes.Points3D").short_name(), "Points3D");
/// ```
#[inline]
pub fn short_name(&self) -> &'static str {
self.sanity_check();
let full_name = self.0.as_str();
if let Some(short_name) = full_name.strip_prefix("rerun.archetypes.") {
short_name
} else if let Some(short_name) = full_name.strip_prefix("rerun.blueprint.archetypes.") {
short_name
} else if let Some(short_name) = full_name.strip_prefix("rerun.") {
short_name
} else {
full_name
}
}
/// Url to the rerun docs for this Rerun archetype.
pub fn doc_url(&self) -> Option<String> {
// This code should be correct as long as this url passes our link checker:
// https://rerun.io/docs/reference/types/archetypes/line_strips3d
let short_name_pascal_case = self.full_name().strip_prefix("rerun.archetypes.")?;
let archetype_name_snake_case = re_case::to_snake_case(short_name_pascal_case);
let base_url = "https://rerun.io/docs/reference/types/archetypes";
Some(format!("{base_url}/{archetype_name_snake_case}"))
}
}
impl re_byte_size::SizeBytes for ArchetypeName {
#[inline]
fn heap_size_bytes(&self) -> u64 {
0
}
}
// ---
re_string_interner::declare_new_type!(
/// An identifier for a component, i.e. a field in an [`Archetype`].
#[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
pub struct ComponentIdentifier;
);
impl re_byte_size::SizeBytes for ComponentIdentifier {
#[inline]
fn heap_size_bytes(&self) -> u64 {
0
}
}