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::Zoned;
20use vortex::layout::segments::SegmentId;
21use vortex::layout::segments::SegmentSource;
22use vortex::session::VortexSession;
23
24use super::ui::SegmentGridState;
25
26#[derive(Default, Copy, Clone, Eq, PartialEq)]
28pub enum Tab {
29 #[default]
34 Layout,
35
36 Segments,
40
41 #[cfg(feature = "native")]
43 Query,
44}
45
46pub struct LayoutCursor {
52 path: Vec<usize>,
53 footer: Footer,
54 layout: LayoutRef,
55 segment_map: Arc<[SegmentSpec]>,
56 segment_source: Arc<dyn SegmentSource>,
57}
58
59impl LayoutCursor {
60 pub fn new(footer: Footer, segment_source: Arc<dyn SegmentSource>) -> Self {
62 Self {
63 path: Vec::new(),
64 layout: footer.layout().clone(),
65 segment_map: Arc::clone(footer.segment_map()),
66 footer,
67 segment_source,
68 }
69 }
70
71 pub fn new_with_path(
75 footer: Footer,
76 segment_source: Arc<dyn SegmentSource>,
77 path: Vec<usize>,
78 ) -> Self {
79 let mut layout = footer.layout().clone();
80
81 for component in path.iter().copied() {
83 layout = layout
84 .child(component)
85 .vortex_expect("Failed to get child layout");
86 }
87
88 Self {
89 segment_map: Arc::clone(footer.segment_map()),
90 path,
91 footer,
92 layout,
93 segment_source,
94 }
95 }
96
97 pub fn child(&self, n: usize) -> Self {
99 let mut path = self.path.clone();
100 path.push(n);
101
102 Self::new_with_path(self.footer.clone(), self.segment_source.clone(), path)
103 }
104
105 pub fn parent(&self) -> Self {
109 let mut path = self.path.clone();
110 path.pop();
111
112 Self::new_with_path(self.footer.clone(), self.segment_source.clone(), path)
113 }
114
115 pub fn flat_layout_metadata_info(&self) -> String {
121 let flat_layout = self.layout.as_::<Flat>();
122 let metadata = Flat::metadata(flat_layout);
123
124 match metadata.0.array_encoding_tree.as_ref() {
125 Some(tree) => {
126 let size = tree.len();
127 format!(
128 "Flat Metadata: array_encoding_tree present ({} bytes)",
129 size
130 )
131 }
132 None => "Flat Metadata: array_encoding_tree not present".to_string(),
133 }
134 }
135
136 pub fn total_size(&self) -> usize {
138 self.layout_segments()
139 .iter()
140 .map(|id| self.segment_spec(*id).length as usize)
141 .sum()
142 }
143
144 fn layout_segments(&self) -> Vec<SegmentId> {
145 self.layout
146 .depth_first_traversal()
147 .map(|layout| layout.vortex_expect("Failed to load layout"))
148 .flat_map(|layout| layout.segment_ids().into_iter())
149 .collect()
150 }
151
152 pub fn is_stats_table(&self) -> bool {
156 let parent = self.parent();
157 parent.layout().is::<Zoned>() && self.path.last().copied().unwrap_or_default() == 1
158 }
159
160 pub fn dtype(&self) -> &DType {
162 self.layout.dtype()
163 }
164
165 pub fn layout(&self) -> &LayoutRef {
167 &self.layout
168 }
169
170 pub fn segment_spec(&self, id: SegmentId) -> &SegmentSpec {
172 &self.segment_map[*id as usize]
173 }
174}
175
176#[derive(Default, PartialEq, Eq)]
180pub enum KeyMode {
181 #[default]
186 Normal,
187
188 Search,
194}
195
196pub struct AppState {
207 pub session: VortexSession,
209
210 pub key_mode: KeyMode,
212
213 pub search_filter: String,
215
216 pub filter: Option<Vec<bool>>,
221
222 pub vxf: VortexFile,
224
225 pub cursor: LayoutCursor,
227
228 pub current_tab: Tab,
230
231 pub layouts_list_state: ListState,
233
234 pub segment_grid_state: SegmentGridState,
236
237 pub frame_size: Size,
239
240 pub tree_scroll_offset: u16,
242
243 pub cached_flat_array: Option<ArrayRef>,
245
246 pub cached_flatbuffer_size: Option<usize>,
248
249 #[cfg(feature = "native")]
251 pub query_state: super::ui::QueryState,
252
253 #[cfg(feature = "native")]
255 pub file_path: String,
256}
257
258impl AppState {
259 #[cfg(feature = "native")]
265 pub async fn new(
266 session: &VortexSession,
267 path: impl AsRef<std::path::Path>,
268 ) -> vortex::error::VortexResult<AppState> {
269 use vortex::file::OpenOptionsSessionExt;
270
271 let session = session.clone();
272 let vxf = session.open_options().open_path(path.as_ref()).await?;
273
274 let cursor = LayoutCursor::new(vxf.footer().clone(), vxf.segment_source());
275
276 let file_path = path
277 .as_ref()
278 .to_str()
279 .map(|s| s.to_string())
280 .unwrap_or_default();
281
282 Ok(AppState {
283 session,
284 vxf,
285 cursor,
286 key_mode: KeyMode::default(),
287 search_filter: String::new(),
288 filter: None,
289 current_tab: Tab::default(),
290 layouts_list_state: ListState::default().with_selected(Some(0)),
291 segment_grid_state: SegmentGridState::default(),
292 frame_size: Size::new(0, 0),
293 tree_scroll_offset: 0,
294 cached_flat_array: None,
295 cached_flatbuffer_size: None,
296 query_state: super::ui::QueryState::default(),
297 file_path,
298 })
299 }
300
301 #[cfg(target_arch = "wasm32")]
307 pub fn from_buffer(
308 session: VortexSession,
309 buffer: vortex::buffer::ByteBuffer,
310 ) -> vortex::error::VortexResult<AppState> {
311 use vortex::file::OpenOptionsSessionExt;
312
313 let vxf = session.open_options().open_buffer(buffer)?;
314 let cursor = LayoutCursor::new(vxf.footer().clone(), vxf.segment_source());
315
316 Ok(AppState {
317 session,
318 vxf,
319 cursor,
320 key_mode: KeyMode::default(),
321 search_filter: String::new(),
322 filter: None,
323 current_tab: Tab::default(),
324 layouts_list_state: ListState::default().with_selected(Some(0)),
325 segment_grid_state: SegmentGridState::default(),
326 frame_size: Size::new(0, 0),
327 tree_scroll_offset: 0,
328 cached_flat_array: None,
329 cached_flatbuffer_size: None,
330 })
331 }
332
333 pub fn clear_search(&mut self) {
335 self.search_filter.clear();
336 self.filter.take();
337 }
338
339 pub fn reset_layout_view_state(&mut self) {
345 self.layouts_list_state = ListState::default().with_selected(Some(0));
346 self.tree_scroll_offset = 0;
347 self.cached_flat_array = None;
348 self.cached_flatbuffer_size = None;
349 }
350
351 #[cfg(feature = "native")]
353 pub(crate) async fn load_flat_data(&mut self) {
354 use vortex::array::MaskFuture;
355 use vortex::array::serde::ArrayParts;
356 use vortex::expr::root;
357
358 let layout = &self.cursor.layout().clone();
359 let row_count = layout.row_count();
360
361 let reader = layout
363 .new_reader("".into(), self.vxf.segment_source(), &self.session)
364 .vortex_expect("Failed to create reader");
365 let array = reader
366 .projection_evaluation(
367 &(0..row_count),
368 &root(),
369 MaskFuture::new_true(
370 usize::try_from(row_count).vortex_expect("row_count overflowed usize"),
371 ),
372 )
373 .vortex_expect("Failed to construct projection")
374 .await
375 .vortex_expect("Failed to read flat array");
376 self.cached_flat_array = Some(array);
377
378 let segment_id = layout.as_::<Flat>().segment_id();
380 let segment = self
381 .cursor
382 .segment_source
383 .request(segment_id)
384 .await
385 .vortex_expect("Failed to read segment");
386 self.cached_flatbuffer_size = Some(
387 ArrayParts::try_from(segment)
388 .vortex_expect("Failed to parse segment")
389 .metadata()
390 .len(),
391 );
392 }
393}