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
use re_log_types::hash::Hash64;
use re_sdk_types::ComponentIdentifier;
use re_sdk_types::blueprint::components::VisualizerInstructionId;
use re_viewer_context::{
QueryContext, VisualizerInstruction, VisualizerInstructionReport, VisualizerReportContext,
VisualizerReportSeverity,
};
use crate::{
BlueprintResolvedResults, BlueprintResolvedResultsExt as _, ChunksWithComponent,
ComponentMappingError, HybridResultsChunkIter,
};
/// Utility for processing queries while executing a visualizer instruction and reporting errors/warnings as they arise.
pub struct VisualizerInstructionQueryResults<'a> {
instruction: &'a VisualizerInstruction,
query_results: &'a BlueprintResolvedResults<'a>,
output: &'a re_viewer_context::VisualizerExecutionOutput,
}
impl<'a> VisualizerInstructionQueryResults<'a> {
/// Create a new query results wrapper.
pub fn new(
instruction: &'a VisualizerInstruction,
query_results: &'a BlueprintResolvedResults<'a>,
output: &'a re_viewer_context::VisualizerExecutionOutput,
) -> Self {
if query_results.any_missing_chunks() {
output.set_missing_chunks();
}
Self {
instruction,
query_results,
output,
}
}
/// The visualizer instruction ID these results are associated with.
pub fn instruction_id(&self) -> VisualizerInstructionId {
self.instruction.id
}
/// Whether the given component has an identity mapping on this visualizer instruction.
///
/// Identity means the component maps directly to itself with no selector,
/// which is also the default when no explicit mapping is present.
pub fn has_identity_mapping_for_component(&self, component: ComponentIdentifier) -> bool {
self.instruction
.component_mappings
.get(&component)
.is_none_or(|source| source.is_identity_mapping(component))
}
/// Returns a zero-copy iterator over all the results for the given `(timeline, component)` pair.
///
/// Reports an error if there's no chunks for the given component.
/// Use this for required components where row IDs are needed for caching or identification.
///
/// Blueprint row IDs are always discarded.
///
/// Call one of the following methods on the returned [`HybridResultsChunkIter`]:
/// * [`HybridResultsChunkIter::slice`]
/// * [`HybridResultsChunkIter::slice_from_struct_field`]
#[inline]
pub fn iter_required(
&self,
component: re_sdk_types::ComponentIdentifier,
) -> HybridResultsChunkIter<'a> {
let chunks_with_component = match ChunksWithComponent::try_from(
self.query_results.get_required_chunks(component),
) {
Ok(chunks) => chunks,
Err(err) => {
// Don't report an error when the component is just still loading or simply not in our range.
if !matches!(
err,
ComponentMappingError::NoComponentDataForQuery(_)
| ComponentMappingError::NoComponentDataForQueryButIsFetchable(_)
) {
let report = VisualizerInstructionReport {
severity: VisualizerReportSeverity::Error,
context: VisualizerReportContext {
component: Some(component),
extra: None,
},
summary: err.summary(),
details: err.details(),
};
self.output.report(self.instruction.id, report);
}
ChunksWithComponent::empty(component)
}
};
HybridResultsChunkIter::new(chunks_with_component, self.query_results.timeline())
}
/// Returns a zero-copy iterator over all the results for the given `(timeline, component)` pair.
///
/// Use this for optional/recommended components where the original row IDs would otherwise
/// interfere with range zipping on latest-at queries.
///
/// **WARNING**: For latest-at queries, the row IDs are always zeroed out to allow for range zipping.
/// Blueprint row IDs are always discarded.
///
/// Call one of the following methods on the returned [`HybridResultsChunkIter`]:
/// * [`HybridResultsChunkIter::slice`]
/// * [`HybridResultsChunkIter::slice_from_struct_field`]
#[inline]
pub fn iter_optional(
&self,
component: re_sdk_types::ComponentIdentifier,
) -> HybridResultsChunkIter<'a> {
let chunks_with_component = match ChunksWithComponent::try_from(
self.query_results.get_optional_chunks(component),
) {
Ok(chunks) => chunks,
Err(err) => {
let report = VisualizerInstructionReport {
severity: VisualizerReportSeverity::Warning,
context: VisualizerReportContext {
component: Some(component),
extra: None,
},
summary: err.summary(),
details: err.details(),
};
self.output.report(self.instruction.id, report);
ChunksWithComponent::empty(component)
}
};
HybridResultsChunkIter::new(chunks_with_component, self.query_results.timeline())
}
#[inline]
pub fn query_result_hash(&self) -> Hash64 {
self.query_results.query_result_hash()
}
pub fn report_unspecified_source(
&self,
severity: VisualizerReportSeverity,
message: impl Into<String>,
) {
self.output
.report_unspecified_source(self.instruction.id, severity, message);
}
/// Report a diagnostic tied to a specific component.
pub fn report_for_component(
&self,
component: re_sdk_types::ComponentIdentifier,
severity: VisualizerReportSeverity,
summary: impl Into<String>,
) {
self.output.report(
self.instruction.id,
VisualizerInstructionReport {
severity,
context: VisualizerReportContext {
component: Some(component),
extra: None,
},
summary: summary.into(),
details: None,
},
);
}
/// Returns the [`QueryContext`] for this result.
#[inline]
pub fn query_context(&self) -> &QueryContext<'_> {
self.query_results.query_context()
}
/// Returns the target entity path for this result.
#[inline]
pub fn entity_path(&self) -> &re_log_types::EntityPath {
self.query_results.entity_path()
}
}