1use std::sync::Arc;
7
8use ratatui::prelude::Size;
9use ratatui::widgets::ListState;
10use vortex::array::ArrayRef;
11use vortex::dtype::DType;
12use vortex::error::VortexExpect;
13use vortex::file::Footer;
14use vortex::file::SegmentSpec;
15use vortex::file::VortexFile;
16use vortex::layout::LayoutRef;
17use vortex::layout::VTable;
18use vortex::layout::layouts::flat::Flat;
19use vortex::layout::layouts::zoned::LegacyStats;
20use vortex::layout::layouts::zoned::Zoned;
21use vortex::layout::segments::SegmentId;
22use vortex::layout::segments::SegmentSource;
23use vortex::session::VortexSession;
24
25use super::ui::SegmentGridState;
26
27#[derive(Default, Copy, Clone, Eq, PartialEq)]
29pub enum Tab {
30 #[default]
35 Layout,
36
37 Segments,
41
42 #[cfg(feature = "native")]
44 Query,
45}
46
47pub struct LayoutCursor {
53 path: Vec<usize>,
54 footer: Footer,
55 layout: LayoutRef,
56 segment_map: Arc<[SegmentSpec]>,
57 segment_source: Arc<dyn SegmentSource>,
58}
59
60impl LayoutCursor {
61 pub fn new(footer: Footer, segment_source: Arc<dyn SegmentSource>) -> Self {
63 Self {
64 path: Vec::new(),
65 layout: Arc::clone(footer.layout()),
66 segment_map: Arc::clone(footer.segment_map()),
67 footer,
68 segment_source,
69 }
70 }
71
72 pub fn new_with_path(
76 footer: Footer,
77 segment_source: Arc<dyn SegmentSource>,
78 path: Vec<usize>,
79 ) -> Self {
80 let mut layout = Arc::clone(footer.layout());
81
82 for component in path.iter().copied() {
84 layout = layout
85 .child(component)
86 .vortex_expect("Failed to get child layout");
87 }
88
89 Self {
90 segment_map: Arc::clone(footer.segment_map()),
91 path,
92 footer,
93 layout,
94 segment_source,
95 }
96 }
97
98 pub fn child(&self, n: usize) -> Self {
100 let mut path = self.path.clone();
101 path.push(n);
102
103 Self::new_with_path(self.footer.clone(), Arc::clone(&self.segment_source), path)
104 }
105
106 pub fn parent(&self) -> Self {
110 let mut path = self.path.clone();
111 path.pop();
112
113 Self::new_with_path(self.footer.clone(), Arc::clone(&self.segment_source), path)
114 }
115
116 pub fn flat_layout_metadata_info(&self) -> String {
122 let flat_layout = self.layout.as_::<Flat>();
123 let metadata = Flat::metadata(flat_layout);
124
125 match metadata.0.array_encoding_tree.as_ref() {
126 Some(tree) => {
127 let size = tree.len();
128 format!(
129 "Flat Metadata: array_encoding_tree present ({} bytes)",
130 size
131 )
132 }
133 None => "Flat Metadata: array_encoding_tree not present".to_string(),
134 }
135 }
136
137 pub fn total_size(&self) -> usize {
139 self.layout_segments()
140 .iter()
141 .map(|id| self.segment_spec(*id).length as usize)
142 .sum()
143 }
144
145 fn layout_segments(&self) -> Vec<SegmentId> {
146 self.layout
147 .depth_first_traversal()
148 .map(|layout| layout.vortex_expect("Failed to load layout"))
149 .flat_map(|layout| layout.segment_ids().into_iter())
150 .collect()
151 }
152
153 pub fn is_stats_table(&self) -> bool {
157 let parent = self.parent();
158 (parent.layout().is::<Zoned>() || parent.layout().is::<LegacyStats>())
159 && self.path.last().copied().unwrap_or_default() == 1
160 }
161
162 pub fn dtype(&self) -> &DType {
164 self.layout.dtype()
165 }
166
167 pub fn layout(&self) -> &LayoutRef {
169 &self.layout
170 }
171
172 pub fn segment_spec(&self, id: SegmentId) -> &SegmentSpec {
174 &self.segment_map[*id as usize]
175 }
176}
177
178#[derive(Default, PartialEq, Eq)]
182pub enum KeyMode {
183 #[default]
188 Normal,
189
190 Search,
196}
197
198pub struct AppState {
209 pub session: VortexSession,
211
212 pub key_mode: KeyMode,
214
215 pub search_filter: String,
217
218 pub filter: Option<Vec<bool>>,
223
224 pub vxf: VortexFile,
226
227 pub cursor: LayoutCursor,
229
230 pub current_tab: Tab,
232
233 pub layouts_list_state: ListState,
235
236 pub segment_grid_state: SegmentGridState,
238
239 pub frame_size: Size,
241
242 pub tree_scroll_offset: u16,
244
245 pub cached_flat_array: Option<ArrayRef>,
247
248 pub cached_flatbuffer_size: Option<usize>,
250
251 #[cfg(feature = "native")]
253 pub query_state: super::ui::QueryState,
254
255 #[cfg(feature = "native")]
257 pub file_path: String,
258}
259
260impl AppState {
261 #[cfg(feature = "native")]
267 pub async fn new(
268 session: &VortexSession,
269 path: impl AsRef<std::path::Path>,
270 ) -> vortex::error::VortexResult<AppState> {
271 use vortex::file::OpenOptionsSessionExt;
272
273 let session = session.clone();
274 let vxf = session.open_options().open_path(path.as_ref()).await?;
275
276 let cursor = LayoutCursor::new(vxf.footer().clone(), vxf.segment_source());
277
278 let file_path = path
279 .as_ref()
280 .to_str()
281 .map(|s| s.to_string())
282 .unwrap_or_default();
283
284 Ok(AppState {
285 session,
286 vxf,
287 cursor,
288 key_mode: KeyMode::default(),
289 search_filter: String::new(),
290 filter: None,
291 current_tab: Tab::default(),
292 layouts_list_state: ListState::default().with_selected(Some(0)),
293 segment_grid_state: SegmentGridState::default(),
294 frame_size: Size::new(0, 0),
295 tree_scroll_offset: 0,
296 cached_flat_array: None,
297 cached_flatbuffer_size: None,
298 query_state: super::ui::QueryState::default(),
299 file_path,
300 })
301 }
302
303 #[cfg(target_arch = "wasm32")]
309 pub fn from_buffer(
310 session: VortexSession,
311 buffer: vortex::buffer::ByteBuffer,
312 ) -> vortex::error::VortexResult<AppState> {
313 use vortex::file::OpenOptionsSessionExt;
314
315 let vxf = session.open_options().open_buffer(buffer)?;
316 let cursor = LayoutCursor::new(vxf.footer().clone(), vxf.segment_source());
317
318 Ok(AppState {
319 session,
320 vxf,
321 cursor,
322 key_mode: KeyMode::default(),
323 search_filter: String::new(),
324 filter: None,
325 current_tab: Tab::default(),
326 layouts_list_state: ListState::default().with_selected(Some(0)),
327 segment_grid_state: SegmentGridState::default(),
328 frame_size: Size::new(0, 0),
329 tree_scroll_offset: 0,
330 cached_flat_array: None,
331 cached_flatbuffer_size: None,
332 })
333 }
334
335 pub fn clear_search(&mut self) {
337 self.search_filter.clear();
338 self.filter.take();
339 }
340
341 pub fn reset_layout_view_state(&mut self) {
347 self.layouts_list_state = ListState::default().with_selected(Some(0));
348 self.tree_scroll_offset = 0;
349 self.cached_flat_array = None;
350 self.cached_flatbuffer_size = None;
351 }
352
353 #[cfg(feature = "native")]
355 pub(crate) async fn load_flat_data(&mut self) {
356 use vortex::array::MaskFuture;
357 use vortex::array::serde::SerializedArray;
358 use vortex::expr::root;
359
360 let layout = &Arc::clone(self.cursor.layout());
361 let row_count = layout.row_count();
362
363 let reader = layout
365 .new_reader(
366 "".into(),
367 self.vxf.segment_source(),
368 &self.session,
369 &Default::default(),
370 )
371 .vortex_expect("Failed to create reader");
372 let array = reader
373 .projection_evaluation(
374 &(0..row_count),
375 &root(),
376 MaskFuture::new_true(
377 usize::try_from(row_count).vortex_expect("row_count overflowed usize"),
378 ),
379 )
380 .vortex_expect("Failed to construct projection")
381 .await
382 .vortex_expect("Failed to read flat array");
383 self.cached_flat_array = Some(array);
384
385 let segment_id = layout.as_::<Flat>().segment_id();
387 let segment = self
388 .cursor
389 .segment_source
390 .request(segment_id)
391 .await
392 .vortex_expect("Failed to read segment");
393 self.cached_flatbuffer_size = Some(
394 SerializedArray::try_from(segment)
395 .vortex_expect("Failed to parse segment")
396 .metadata()
397 .len(),
398 );
399 }
400}