use std::{io, ops::DerefMut, path::Path, process::ExitCode};
use color_eyre::Result;
use crossterm::{
cursor::Show,
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use fuzzy_matcher::FuzzyMatcher;
use planus_translation::{translate_files_with_options, ConverterOptions};
use planus_types::intermediate::{AbsolutePath, DeclarationIndex, DeclarationKind};
use ratatui::{backend::CrosstermBackend, Terminal};
use crate::{run_inspector, Inspector};
pub fn run_app(
schema_file: &Path,
root_type: &str,
buffer: &[u8],
converter_options: ConverterOptions,
) -> Result<ExitCode> {
let Some(declarations) = translate_files_with_options(&[schema_file], converter_options) else {
return Ok(ExitCode::FAILURE);
};
let root_type_path = AbsolutePath(root_type.split('.').map(|s| s.to_owned()).collect());
let Some((root_table_index, _, root_declaration)) =
declarations.declarations.get_full(&root_type_path)
else {
let matcher = fuzzy_matcher::skim::SkimMatcherV2::default();
let mut matching_paths = declarations
.declarations
.iter()
.filter(|(_path, declaration)| matches!(declaration.kind, DeclarationKind::Table(_)))
.filter_map(|(path, _declaration)| {
let path = path.to_string();
Some((
std::cmp::Reverse(matcher.fuzzy_match(&path, root_type)?),
path,
))
})
.collect::<Vec<_>>();
matching_paths.sort();
if matching_paths.is_empty() {
println!("Could not find root type {root_type:?}.");
} else {
println!(
"Could not find root type {root_type:?}. These are a few of the closest matching tables:"
);
for (_score, path) in matching_paths.iter().take(5) {
println!("- {path}");
}
}
return Ok(ExitCode::FAILURE);
};
if !matches!(root_declaration.kind, DeclarationKind::Table(_)) {
println!(
"Type {} is not a table, but a {}",
root_type,
root_declaration.kind.kind_as_str()
);
return Ok(ExitCode::FAILURE);
}
let inspector = Inspector::new(
planus_buffer_inspection::InspectableFlatbuffer {
declarations: &declarations,
buffer,
},
DeclarationIndex(root_table_index),
);
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |x| {
let mut stdout = io::stdout();
cleanup_terminal(&mut stdout).ok();
hook(x);
}));
let res = run_inspector(&mut terminal, inspector);
cleanup_terminal(terminal.backend_mut().deref_mut())?;
if let Err(err) = res {
println!("{err:?}");
Ok(ExitCode::FAILURE)
} else {
Ok(ExitCode::SUCCESS)
}
}
fn cleanup_terminal(stdout: &mut impl std::io::Write) -> Result<()> {
disable_raw_mode()?;
execute!(stdout, LeaveAlternateScreen, Show)?;
Ok(())
}