use clap::{CommandFactory, Parser};
use indexmap::IndexMap;
use polars::frame::DataFrame;
use polars::prelude::Schema;
use std::io::IsTerminal;
use std::sync::Arc;
use tabiew::app::App;
use tabiew::args::Args;
use tabiew::handler::event::{Event, read_event};
use tabiew::handler::message::Message;
use tabiew::io::DataSource;
use tabiew::io::reader::ReaderSource;
use tabiew::io::reader::{BuildReader, NamedFrames};
use tabiew::misc::config::config;
use tabiew::misc::download::download_to_temp;
use tabiew::misc::osc52::flush_osc52_buffer;
use tabiew::misc::sql::{TableSource, sql};
use tabiew::misc::type_ext::UnwrapOrGracefulShutdown;
use tabiew::misc::type_inferer::TypeInferer;
use tabiew::tui::component::{Component, FocusState};
use tabiew::tui::pane::TableDescription;
use tabiew::tui::terminal::{draw, start_tui, stop_tui};
use tabiew::AppResult;
use tabiew::tui::Pane;
fn main() {
let args = {
let args_os = std::env::args_os();
if args_os.len() == 1 && std::io::stdin().is_terminal() {
return Args::command().print_help().unwrap_or_graceful_shutdown();
} else {
Args::parse_from(args_os)
}
};
let _ = config().reload();
let type_infer = TypeInferer::from_args(&args);
let mut name_dfs = Vec::new();
let mut multiparts = IndexMap::<Arc<Schema>, (String, DataFrame)>::new();
for resource in args.multiparts.iter() {
for (name, new_df) in try_read_path(&args, resource).unwrap_or_graceful_shutdown() {
let schema = new_df.schema().clone();
if let Some((_, df)) = multiparts.get_mut(&schema) {
df.vstack_mut_owned(new_df).unwrap_or_graceful_shutdown();
} else {
multiparts.insert(schema, (name, new_df));
}
}
}
for (_, (name, mut df)) in multiparts {
df.rechunk_mut_par();
type_infer.update(&mut df);
let name = sql().register(&name, df.clone(), TableSource::File(name.clone().into()));
name_dfs.push((name, df));
}
for resource in args.resources.iter() {
for (name, mut df) in try_read_path(&args, resource).unwrap_or_graceful_shutdown() {
type_infer.update(&mut df);
let name = sql().register(&name, df.clone(), resource.clone());
name_dfs.push((name, df))
}
}
if name_dfs.is_empty() {
for (name, mut df) in args
.build_reader("")
.unwrap_or_graceful_shutdown()
.read_to_data_frames(ReaderSource::Stdin)
.unwrap_or_graceful_shutdown()
{
type_infer.update(&mut df);
let name = sql().register(&name, df.clone(), TableSource::Stdin);
name_dfs.push((name, df))
}
}
let _ = start_app(name_dfs);
}
fn start_app(tabs: Vec<(String, DataFrame)>) -> AppResult<()> {
let tabs = tabs
.into_iter()
.map(|(name, df)| Pane::new(df, TableDescription::Table(name)))
.collect();
start_tui()?;
let mut app = App::new(tabs);
while app.running() {
draw(&mut app)?;
flush_osc52_buffer();
match read_event()? {
Event::Tick => app.tick(),
Event::Key(key_event) => {
#[cfg(target_os = "windows")]
{
use crossterm::event::KeyEventKind;
if matches!(key_event.kind, KeyEventKind::Press | KeyEventKind::Repeat) {
app.handle(key_event);
}
}
#[cfg(not(target_os = "windows"))]
{
use tabiew::tui::component::Component;
app.handle(key_event);
}
}
Event::Mouse(_) => {}
Event::Resize(_, _) => {}
Event::FocusGained => {}
Event::FocusLost => {}
Event::Paste(_) => {}
}
while let Some(action) = Message::dequeue() {
app.update(&action, FocusState::Focused);
}
flush_osc52_buffer();
}
stop_tui()?;
Ok(())
}
fn try_read_path(args: &Args, resource: &DataSource) -> AppResult<NamedFrames> {
match resource {
DataSource::Stdin => args
.build_reader("")?
.read_to_data_frames(ReaderSource::Stdin),
DataSource::File(path_buf) => args
.build_reader(path_buf)?
.read_to_data_frames(ReaderSource::File(path_buf.clone())),
DataSource::Url(url) => {
let file = download_to_temp(url)?;
args.build_reader(file.path())?
.read_to_data_frames(ReaderSource::File(file.path().to_owned()))
}
}
}