Skip to main content

vortex_tui/
segments.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Display segment information for Vortex files.
5
6use std::path::PathBuf;
7
8use serde::Serialize;
9use vortex::error::VortexResult;
10use vortex::file::OpenOptionsSessionExt;
11use vortex::session::VortexSession;
12
13use crate::segment_tree::collect_segment_tree;
14
15/// Command-line arguments for the segments command.
16#[derive(Debug, clap::Parser)]
17pub struct SegmentsArgs {
18    /// Path to the Vortex file
19    pub file: PathBuf,
20}
21
22#[derive(Serialize)]
23struct SegmentsOutput {
24    /// Columns in display order
25    columns: Vec<ColumnInfo>,
26}
27
28#[derive(Serialize)]
29struct ColumnInfo {
30    /// Field name (column header)
31    name: String,
32    /// Segments within this column
33    segments: Vec<SegmentInfo>,
34}
35
36#[derive(Serialize)]
37struct SegmentInfo {
38    /// Segment name (e.g., "[0]", "data", etc.)
39    name: String,
40    /// Row range start
41    row_offset: u64,
42    /// Number of rows
43    row_count: u64,
44    /// Byte offset in file
45    byte_offset: u64,
46    /// Length in bytes
47    byte_length: u32,
48    /// Alignment requirement
49    alignment: usize,
50    /// Gap from previous segment end
51    byte_gap: u64,
52}
53
54/// Display segment information for a Vortex file.
55///
56/// # Errors
57///
58/// Returns an error if the file cannot be opened or read.
59pub async fn exec_segments(session: &VortexSession, args: SegmentsArgs) -> VortexResult<()> {
60    let vxf = session.open_options().open_path(args.file).await?;
61    let footer = vxf.footer();
62
63    let mut segment_tree = collect_segment_tree(footer.layout().as_ref(), footer.segment_map());
64
65    // Convert to output format
66    let columns: Vec<ColumnInfo> = segment_tree
67        .segment_ordering
68        .iter()
69        .filter_map(|name| {
70            let mut segments = segment_tree.segments.remove(name)?;
71
72            // Sort by byte offset
73            segments.sort_by(|a, b| a.spec.offset.cmp(&b.spec.offset));
74
75            // Convert to output format, computing byte gaps
76            let mut current_offset = 0u64;
77            let segment_infos: Vec<SegmentInfo> = segments
78                .into_iter()
79                .map(|seg| {
80                    let byte_gap = if current_offset == 0 {
81                        0
82                    } else {
83                        seg.spec.offset.saturating_sub(current_offset)
84                    };
85                    current_offset = seg.spec.offset + seg.spec.length as u64;
86
87                    SegmentInfo {
88                        name: seg.name.to_string(),
89                        row_offset: seg.row_offset,
90                        row_count: seg.row_count,
91                        byte_offset: seg.spec.offset,
92                        byte_length: seg.spec.length,
93                        alignment: *seg.spec.alignment,
94                        byte_gap,
95                    }
96                })
97                .collect();
98
99            Some(ColumnInfo {
100                name: name.to_string(),
101                segments: segment_infos,
102            })
103        })
104        .collect();
105
106    let output = SegmentsOutput { columns };
107
108    let json_output = serde_json::to_string_pretty(&output)
109        .map_err(|e| vortex::error::vortex_err!("Failed to serialize JSON: {e}"))?;
110    println!("{json_output}");
111
112    Ok(())
113}