feat: adjust arg processing so script+command modes are exclusive
This commit is contained in:
62
src/main.rs
62
src/main.rs
@ -13,6 +13,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
use command::CommandResult;
|
||||||
use draw::run_tui;
|
use draw::run_tui;
|
||||||
use model::Model;
|
use model::Model;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@ -93,32 +94,38 @@ fn get_import_data(paths: &[PathBuf]) -> Option<Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HeadlessMode {
|
||||||
|
Commands(Vec<String>),
|
||||||
|
Script(Option<PathBuf>),
|
||||||
|
}
|
||||||
struct HeadlessArgs {
|
struct HeadlessArgs {
|
||||||
file_path: Option<PathBuf>,
|
file_path: Option<PathBuf>,
|
||||||
commands: Vec<String>,
|
mode: HeadlessMode,
|
||||||
script: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Runnable for HeadlessArgs {
|
impl Runnable for HeadlessArgs {
|
||||||
fn run(self: Box<Self>) -> Result<()> {
|
fn run(self: Box<Self>) -> Result<()> {
|
||||||
let mut model = get_initial_model(&self.file_path)?;
|
let mut model = get_initial_model(&self.file_path)?;
|
||||||
let mut cmds: Vec<String> = self.commands;
|
let mut exit_code = 0;
|
||||||
if let Some(script_path) = self.script {
|
match self.mode {
|
||||||
|
HeadlessMode::Script(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)?;
|
||||||
|
let mut cmds: Vec<String> = Vec::new();
|
||||||
for line in content.lines() {
|
for line in content.lines() {
|
||||||
let trimmed = line.trim();
|
let trimmed = line.trim();
|
||||||
if !trimmed.is_empty() && !trimmed.starts_with("//") && !trimmed.starts_with('#') {
|
if !trimmed.is_empty()
|
||||||
|
&& !trimmed.starts_with("//")
|
||||||
|
&& !trimmed.starts_with('#')
|
||||||
|
{
|
||||||
cmds.push(trimmed.to_string());
|
cmds.push(trimmed.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut exit_code = 0;
|
|
||||||
for raw_cmd in &cmds {
|
for raw_cmd in &cmds {
|
||||||
let parsed: command::Command = match serde_json::from_str(raw_cmd) {
|
let parsed: command::Command = match serde_json::from_str(raw_cmd) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let r = command::CommandResult::err(format!("JSON parse error: {e}"));
|
let r = CommandResult::err(format!("JSON parse error: {e}"));
|
||||||
println!("{}", serde_json::to_string(&r)?);
|
println!("{}", serde_json::to_string(&r)?);
|
||||||
exit_code = 1;
|
exit_code = 1;
|
||||||
continue;
|
continue;
|
||||||
@ -130,6 +137,27 @@ impl Runnable for HeadlessArgs {
|
|||||||
}
|
}
|
||||||
println!("{}", serde_json::to_string(&result)?);
|
println!("{}", serde_json::to_string(&result)?);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
HeadlessMode::Commands(cmds) => {
|
||||||
|
for raw_cmd in &cmds {
|
||||||
|
let parsed: command::Command = match serde_json::from_str(raw_cmd) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
let r = CommandResult::err(format!("JSON parse error: {e}"));
|
||||||
|
println!("{}", serde_json::to_string(&r)?);
|
||||||
|
exit_code = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = command::dispatch(&mut model, &parsed);
|
||||||
|
if !result.ok {
|
||||||
|
exit_code = 1;
|
||||||
|
}
|
||||||
|
println!("{}", serde_json::to_string(&result)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(path) = self.file_path {
|
if let Some(path) = self.file_path {
|
||||||
persistence::save(&model, &path)?;
|
persistence::save(&model, &path)?;
|
||||||
@ -150,8 +178,8 @@ impl Runnable for HelpArgs {
|
|||||||
println!(
|
println!(
|
||||||
" improvise --import a.csv b.csv Import multiple CSVs (filenames become a category)"
|
" improvise --import a.csv b.csv Import multiple CSVs (filenames become a category)"
|
||||||
);
|
);
|
||||||
println!(" improvise --cmd '{{...}}' Run a JSON command (headless, repeatable)");
|
println!(" improvise --cmd '{{...}}' Run JSON command(s) headless (repeatable)");
|
||||||
println!(" improvise --script cmds.jsonl Run commands from file (headless)");
|
println!(" improvise --script cmds.jsonl Run commands from file headless (exclusive with --cmd)");
|
||||||
println!("\nTUI KEYS (vim-style):");
|
println!("\nTUI KEYS (vim-style):");
|
||||||
println!(" : Command mode (:q :w :import :add-cat :formula …)");
|
println!(" : Command mode (:q :w :import :add-cat :formula …)");
|
||||||
println!(" hjkl / ↑↓←→ Navigate grid");
|
println!(" hjkl / ↑↓←→ Navigate grid");
|
||||||
@ -210,11 +238,17 @@ fn parse_args(args: Vec<String>) -> Box<dyn Runnable> {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !headless_cmds.is_empty() || headless_script.is_some() {
|
if !headless_cmds.is_empty() && headless_script.is_some() {
|
||||||
|
eprintln!("Error: --cmd and --script cannot be used together");
|
||||||
|
std::process::exit(1);
|
||||||
|
} else if !headless_cmds.is_empty() || headless_script.is_some() {
|
||||||
Box::new(HeadlessArgs {
|
Box::new(HeadlessArgs {
|
||||||
file_path,
|
file_path,
|
||||||
commands: headless_cmds,
|
mode: if headless_script.is_some() {
|
||||||
script: headless_script,
|
HeadlessMode::Script(headless_script)
|
||||||
|
} else {
|
||||||
|
HeadlessMode::Commands(headless_cmds)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Box::new(CmdLineArgs {
|
Box::new(CmdLineArgs {
|
||||||
|
|||||||
Reference in New Issue
Block a user