refactor: split main code paths for clarity.

This commit is contained in:
Edward Langley
2026-03-31 20:50:27 -07:00
parent aae8392f46
commit c32e800128

View File

@ -34,22 +34,70 @@ use ui::import_wizard_ui::ImportWizardWidget;
use ui::tile_bar::TileBar; use ui::tile_bar::TileBar;
use ui::view_panel::ViewPanel; use ui::view_panel::ViewPanel;
fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
let arg_config = parse_args(args);
arg_config.run()
}
trait Runnable {
fn run(self: Box<Self>) -> Result<()>;
}
struct CmdLineArgs { struct CmdLineArgs {
file_path: Option<PathBuf>, file_path: Option<PathBuf>,
import_path: Option<PathBuf>, import_path: Option<PathBuf>,
} }
enum ArgConfig { impl Runnable for CmdLineArgs {
Help, fn run(self: Box<Self>) -> Result<()> {
Headless { // Load or create model
file_path: Option<PathBuf>, let model = get_initial_model(&self.file_path)?;
commands: Vec<String>,
script: Option<PathBuf>, // Pre-TUI import: parse JSON and open wizard
}, let import_json = if let Some(ref path) = self.import_path {
Default(CmdLineArgs), match std::fs::read_to_string(path) {
Err(e) => {
eprintln!("Cannot read '{}': {e}", path.display());
return Ok(());
}
Ok(content) => match serde_json::from_str::<serde_json::Value>(&content) {
Err(e) => {
eprintln!("JSON parse error: {e}");
return Ok(());
}
Ok(json) => Some(json),
},
}
} else {
None
};
run_tui(model, self.file_path, import_json)
}
} }
fn parse_args(args: Vec<String>) -> ArgConfig { struct HeadlessArgs {
file_path: Option<PathBuf>,
commands: Vec<String>,
script: Option<PathBuf>,
}
impl Runnable for HeadlessArgs {
fn run(self: Box<Self>) -> Result<()> {
run_headless(self.file_path, self.commands, self.script)
}
}
struct HelpArgs {}
impl Runnable for HelpArgs {
fn run(self: Box<Self>) -> Result<()> {
print_usage()
}
}
fn parse_args(args: Vec<String>) -> Box<dyn Runnable> {
let mut file_path: Option<PathBuf> = None; let mut file_path: Option<PathBuf> = None;
let mut headless_cmds: Vec<String> = Vec::new(); let mut headless_cmds: Vec<String> = Vec::new();
let mut headless_script: Option<PathBuf> = None; let mut headless_script: Option<PathBuf> = None;
@ -73,7 +121,7 @@ fn parse_args(args: Vec<String>) -> ArgConfig {
import_path = args.get(i).map(PathBuf::from); import_path = args.get(i).map(PathBuf::from);
} }
"--help" | "-h" => { "--help" | "-h" => {
return ArgConfig::Help; return Box::new(HelpArgs {});
} }
arg if !arg.starts_with('-') => { arg if !arg.starts_with('-') => {
file_path = Some(PathBuf::from(arg)); file_path = Some(PathBuf::from(arg));
@ -84,14 +132,14 @@ fn parse_args(args: Vec<String>) -> ArgConfig {
} }
if !headless_cmds.is_empty() || headless_script.is_some() { if !headless_cmds.is_empty() || headless_script.is_some() {
return ArgConfig::Headless { return Box::new(HeadlessArgs {
file_path, file_path,
commands: headless_cmds, commands: headless_cmds,
script: headless_script, script: headless_script,
}; });
} }
return ArgConfig::Default(CmdLineArgs { return Box::new(CmdLineArgs {
file_path, file_path,
import_path, import_path,
}); });
@ -117,58 +165,12 @@ fn get_initial_model(file_path: &Option<PathBuf>) -> Result<Model> {
}; };
} }
fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
let arg_config = parse_args(args);
match arg_config {
ArgConfig::Default(cmd_line_args) => {
// Load or create model
let model = get_initial_model(&cmd_line_args.file_path)?;
// Pre-TUI import: parse JSON and open wizard
let import_json = if let Some(ref path) = cmd_line_args.import_path {
match std::fs::read_to_string(path) {
Err(e) => {
eprintln!("Cannot read '{}': {e}", path.display());
return Ok(());
}
Ok(content) => match serde_json::from_str::<serde_json::Value>(&content) {
Err(e) => {
eprintln!("JSON parse error: {e}");
return Ok(());
}
Ok(json) => Some(json),
},
}
} else {
None
};
run_tui(model, cmd_line_args.file_path, import_json)
}
ArgConfig::Headless {
file_path,
commands,
script,
} => {
let mut model = get_initial_model(&file_path)?;
return run_headless(&mut model, file_path, commands, script);
}
ArgConfig::Help => {
print_usage();
Ok(())
}
}
}
fn run_headless( fn run_headless(
model: &mut Model,
file_path: Option<PathBuf>, file_path: Option<PathBuf>,
inline_cmds: Vec<String>, inline_cmds: Vec<String>,
script: Option<PathBuf>, script: Option<PathBuf>,
) -> Result<()> { ) -> Result<()> {
let mut model = get_initial_model(&file_path)?;
let mut cmds: Vec<String> = inline_cmds; let mut cmds: Vec<String> = inline_cmds;
if let Some(script_path) = script { if let Some(script_path) = script {
let content = std::fs::read_to_string(&script_path)?; let content = std::fs::read_to_string(&script_path)?;
@ -191,7 +193,7 @@ fn run_headless(
continue; continue;
} }
}; };
let result = command::dispatch(model, &parsed); let result = command::dispatch(&mut model, &parsed);
if !result.ok { if !result.ok {
exit_code = 1; exit_code = 1;
} }
@ -199,7 +201,7 @@ fn run_headless(
} }
if let Some(path) = file_path { if let Some(path) = file_path {
persistence::save(model, &path)?; persistence::save(&mut model, &path)?;
} }
std::process::exit(exit_code); std::process::exit(exit_code);
@ -560,7 +562,7 @@ fn draw_welcome(f: &mut Frame, area: Rect, _app: &App) {
// ── Usage ───────────────────────────────────────────────────────────────────── // ── Usage ─────────────────────────────────────────────────────────────────────
fn print_usage() { fn print_usage() -> Result<()> {
println!("improvise — multi-dimensional data modeling TUI\n"); println!("improvise — multi-dimensional data modeling TUI\n");
println!("USAGE:"); println!("USAGE:");
println!(" improvise [file.improv] Open or create a model"); println!(" improvise [file.improv] Open or create a model");
@ -583,4 +585,5 @@ fn print_usage() {
println!(" F C V Toggle Formulas / Categories / Views panel"); println!(" F C V Toggle Formulas / Categories / Views panel");
println!(" ZZ Save and quit"); println!(" ZZ Save and quit");
println!(" ? Help"); println!(" ? Help");
Ok(())
} }