Skip to main content

vortex_tui/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Vortex TUI library for interactively browsing and inspecting Vortex files.
5//!
6//! This crate provides both a CLI tool (`vx`) and a library API for working with Vortex files.
7//! Users can bring their own [`VortexSession`] to enable custom encodings and extensions.
8//!
9//! # Example
10//!
11//! ```ignore
12//! use vortex::session::VortexSession;
13//! use vortex::io::session::RuntimeSessionExt;
14//! use vortex_tui::browse;
15//!
16//! let session = VortexSession::default().with_tokio();
17//! browse::exec_tui(&session, "my_file.vortex").await?;
18//! ```
19
20#![deny(clippy::missing_errors_doc)]
21#![deny(clippy::missing_panics_doc)]
22#![deny(clippy::missing_safety_doc)]
23#![deny(missing_docs)]
24
25use std::ffi::OsString;
26use std::path::PathBuf;
27
28use clap::CommandFactory;
29use clap::Parser;
30use vortex::error::VortexExpect;
31use vortex::session::VortexSession;
32
33pub mod browse;
34pub mod convert;
35pub mod datafusion_helper;
36pub mod inspect;
37pub mod query;
38pub mod segment_tree;
39pub mod segments;
40pub mod tree;
41
42#[derive(clap::Parser)]
43#[command(version)]
44struct Cli {
45    #[clap(subcommand)]
46    command: Commands,
47}
48
49#[derive(Debug, clap::Subcommand)]
50enum Commands {
51    /// Print tree views of a Vortex file (layout tree or array tree)
52    Tree(tree::TreeArgs),
53    /// Convert a Parquet file to a Vortex file. Chunking occurs on Parquet RowGroup boundaries.
54    Convert(#[command(flatten)] convert::ConvertArgs),
55    /// Interactively browse the Vortex file.
56    Browse { file: PathBuf },
57    /// Inspect Vortex file footer and metadata
58    Inspect(inspect::InspectArgs),
59    /// Execute a SQL query against a Vortex file using DataFusion
60    Query(query::QueryArgs),
61    /// Display segment information for a Vortex file
62    Segments(segments::SegmentsArgs),
63}
64
65impl Commands {
66    fn file_path(&self) -> &PathBuf {
67        match self {
68            Commands::Tree(args) => match &args.mode {
69                tree::TreeMode::Array { file, .. } => file,
70                tree::TreeMode::Layout { file, .. } => file,
71            },
72            Commands::Browse { file } => file,
73            Commands::Convert(flags) => &flags.file,
74            Commands::Inspect(args) => &args.file,
75            Commands::Query(args) => &args.file,
76            Commands::Segments(args) => &args.file,
77        }
78    }
79}
80
81/// Main entrypoint for `vx` that launches a [`VortexSession`].
82///
83/// Parses arguments from [`std::env::args_os`]. See [`launch_from`] to supply explicit arguments.
84///
85/// # Errors
86///
87/// Raises any errors from subcommands.
88pub async fn launch(session: &VortexSession) -> anyhow::Result<()> {
89    launch_from(session, std::env::args_os()).await
90}
91
92/// Launch `vx` with explicit command-line arguments.
93///
94/// This is useful when embedding the TUI inside another process (e.g. Python) where
95/// [`std::env::args`] may not reflect the intended arguments.
96///
97/// # Errors
98///
99/// Raises any errors from subcommands.
100pub async fn launch_from(
101    session: &VortexSession,
102    args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
103) -> anyhow::Result<()> {
104    let _ = env_logger::try_init();
105
106    let cli = Cli::parse_from(args);
107
108    let path = cli.command.file_path();
109    if !std::fs::exists(path)? {
110        Cli::command()
111            .error(
112                clap::error::ErrorKind::Io,
113                format!(
114                    "File '{}' does not exist.",
115                    path.to_str().vortex_expect("file path")
116                ),
117            )
118            .exit()
119    }
120
121    match cli.command {
122        Commands::Tree(args) => tree::exec_tree(session, args).await?,
123        Commands::Convert(flags) => convert::exec_convert(session, flags).await?,
124        Commands::Browse { file } => browse::exec_tui(session, file).await?,
125        Commands::Inspect(args) => inspect::exec_inspect(session, args).await?,
126        Commands::Query(args) => query::exec_query(session, args).await?,
127        Commands::Segments(args) => segments::exec_segments(session, args).await?,
128    };
129
130    Ok(())
131}