use std::sync::Arc;
use ratatui::prelude::Size;
use ratatui::widgets::ListState;
use vortex::array::ArrayRef;
use vortex::dtype::DType;
use vortex::error::VortexExpect;
use vortex::file::Footer;
use vortex::file::SegmentSpec;
use vortex::file::VortexFile;
use vortex::layout::LayoutRef;
use vortex::layout::VTable;
use vortex::layout::layouts::flat::Flat;
use vortex::layout::layouts::zoned::LegacyStats;
use vortex::layout::layouts::zoned::Zoned;
use vortex::layout::segments::SegmentId;
use vortex::layout::segments::SegmentSource;
use vortex::session::VortexSession;
use super::ui::SegmentGridState;
#[derive(Default, Copy, Clone, Eq, PartialEq)]
pub enum Tab {
#[default]
Layout,
Segments,
#[cfg(feature = "native")]
Query,
}
pub struct LayoutCursor {
path: Vec<usize>,
footer: Footer,
layout: LayoutRef,
segment_map: Arc<[SegmentSpec]>,
segment_source: Arc<dyn SegmentSource>,
}
impl LayoutCursor {
pub fn new(footer: Footer, segment_source: Arc<dyn SegmentSource>) -> Self {
Self {
path: Vec::new(),
layout: Arc::clone(footer.layout()),
segment_map: Arc::clone(footer.segment_map()),
footer,
segment_source,
}
}
pub fn new_with_path(
footer: Footer,
segment_source: Arc<dyn SegmentSource>,
path: Vec<usize>,
) -> Self {
let mut layout = Arc::clone(footer.layout());
for component in path.iter().copied() {
layout = layout
.child(component)
.vortex_expect("Failed to get child layout");
}
Self {
segment_map: Arc::clone(footer.segment_map()),
path,
footer,
layout,
segment_source,
}
}
pub fn child(&self, n: usize) -> Self {
let mut path = self.path.clone();
path.push(n);
Self::new_with_path(self.footer.clone(), Arc::clone(&self.segment_source), path)
}
pub fn parent(&self) -> Self {
let mut path = self.path.clone();
path.pop();
Self::new_with_path(self.footer.clone(), Arc::clone(&self.segment_source), path)
}
pub fn flat_layout_metadata_info(&self) -> String {
let flat_layout = self.layout.as_::<Flat>();
let metadata = Flat::metadata(flat_layout);
match metadata.0.array_encoding_tree.as_ref() {
Some(tree) => {
let size = tree.len();
format!(
"Flat Metadata: array_encoding_tree present ({} bytes)",
size
)
}
None => "Flat Metadata: array_encoding_tree not present".to_string(),
}
}
pub fn total_size(&self) -> usize {
self.layout_segments()
.iter()
.map(|id| self.segment_spec(*id).length as usize)
.sum()
}
fn layout_segments(&self) -> Vec<SegmentId> {
self.layout
.depth_first_traversal()
.map(|layout| layout.vortex_expect("Failed to load layout"))
.flat_map(|layout| layout.segment_ids().into_iter())
.collect()
}
pub fn is_stats_table(&self) -> bool {
let parent = self.parent();
(parent.layout().is::<Zoned>() || parent.layout().is::<LegacyStats>())
&& self.path.last().copied().unwrap_or_default() == 1
}
pub fn dtype(&self) -> &DType {
self.layout.dtype()
}
pub fn layout(&self) -> &LayoutRef {
&self.layout
}
pub fn segment_spec(&self, id: SegmentId) -> &SegmentSpec {
&self.segment_map[*id as usize]
}
}
#[derive(Default, PartialEq, Eq)]
pub enum KeyMode {
#[default]
Normal,
Search,
}
pub struct AppState {
pub session: VortexSession,
pub key_mode: KeyMode,
pub search_filter: String,
pub filter: Option<Vec<bool>>,
pub vxf: VortexFile,
pub cursor: LayoutCursor,
pub current_tab: Tab,
pub layouts_list_state: ListState,
pub segment_grid_state: SegmentGridState,
pub frame_size: Size,
pub tree_scroll_offset: u16,
pub cached_flat_array: Option<ArrayRef>,
pub cached_flatbuffer_size: Option<usize>,
#[cfg(feature = "native")]
pub query_state: super::ui::QueryState,
#[cfg(feature = "native")]
pub file_path: String,
}
impl AppState {
#[cfg(feature = "native")]
pub async fn new(
session: &VortexSession,
path: impl AsRef<std::path::Path>,
) -> vortex::error::VortexResult<AppState> {
use vortex::file::OpenOptionsSessionExt;
let session = session.clone();
let vxf = session.open_options().open_path(path.as_ref()).await?;
let cursor = LayoutCursor::new(vxf.footer().clone(), vxf.segment_source());
let file_path = path
.as_ref()
.to_str()
.map(|s| s.to_string())
.unwrap_or_default();
Ok(AppState {
session,
vxf,
cursor,
key_mode: KeyMode::default(),
search_filter: String::new(),
filter: None,
current_tab: Tab::default(),
layouts_list_state: ListState::default().with_selected(Some(0)),
segment_grid_state: SegmentGridState::default(),
frame_size: Size::new(0, 0),
tree_scroll_offset: 0,
cached_flat_array: None,
cached_flatbuffer_size: None,
query_state: super::ui::QueryState::default(),
file_path,
})
}
#[cfg(target_arch = "wasm32")]
pub fn from_buffer(
session: VortexSession,
buffer: vortex::buffer::ByteBuffer,
) -> vortex::error::VortexResult<AppState> {
use vortex::file::OpenOptionsSessionExt;
let vxf = session.open_options().open_buffer(buffer)?;
let cursor = LayoutCursor::new(vxf.footer().clone(), vxf.segment_source());
Ok(AppState {
session,
vxf,
cursor,
key_mode: KeyMode::default(),
search_filter: String::new(),
filter: None,
current_tab: Tab::default(),
layouts_list_state: ListState::default().with_selected(Some(0)),
segment_grid_state: SegmentGridState::default(),
frame_size: Size::new(0, 0),
tree_scroll_offset: 0,
cached_flat_array: None,
cached_flatbuffer_size: None,
})
}
pub fn clear_search(&mut self) {
self.search_filter.clear();
self.filter.take();
}
pub fn reset_layout_view_state(&mut self) {
self.layouts_list_state = ListState::default().with_selected(Some(0));
self.tree_scroll_offset = 0;
self.cached_flat_array = None;
self.cached_flatbuffer_size = None;
}
#[cfg(feature = "native")]
pub(crate) async fn load_flat_data(&mut self) {
use vortex::array::MaskFuture;
use vortex::array::serde::SerializedArray;
use vortex::expr::root;
let layout = &Arc::clone(self.cursor.layout());
let row_count = layout.row_count();
let reader = layout
.new_reader(
"".into(),
self.vxf.segment_source(),
&self.session,
&Default::default(),
)
.vortex_expect("Failed to create reader");
let array = reader
.projection_evaluation(
&(0..row_count),
&root(),
MaskFuture::new_true(
usize::try_from(row_count).vortex_expect("row_count overflowed usize"),
),
)
.vortex_expect("Failed to construct projection")
.await
.vortex_expect("Failed to read flat array");
self.cached_flat_array = Some(array);
let segment_id = layout.as_::<Flat>().segment_id();
let segment = self
.cursor
.segment_source
.request(segment_id)
.await
.vortex_expect("Failed to read segment");
self.cached_flatbuffer_size = Some(
SerializedArray::try_from(segment)
.vortex_expect("Failed to parse segment")
.metadata()
.len(),
);
}
}