diff --git a/src/command/dispatch.rs b/src/command/dispatch.rs index dd8d26a..c837f09 100644 --- a/src/command/dispatch.rs +++ b/src/command/dispatch.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use super::types::{CellValueArg, Command, CommandResult}; use crate::formula::parse_formula; use crate::import::analyzer::{analyze_records, extract_array_at_path, FieldKind}; @@ -169,11 +171,11 @@ pub fn dispatch(model: &mut Model, cmd: &Command) -> CommandResult { fn import_headless( model: &mut Model, - path: &str, + path: &PathBuf, model_name: Option<&str>, array_path: Option<&str>, ) -> CommandResult { - let is_csv = path.ends_with(".csv"); + let is_csv = path.extension().is_some_and(|ext| ext.eq_ignore_ascii_case("csv")); let records = if is_csv { // Parse CSV file @@ -185,7 +187,7 @@ fn import_headless( // Parse JSON file let content = match std::fs::read_to_string(path) { Ok(c) => c, - Err(e) => return CommandResult::err(format!("Cannot read '{path}': {e}")), + Err(e) => return CommandResult::err(format!("Cannot read '{}': {e}", path.display())), }; let value: serde_json::Value = match serde_json::from_str(&content) { Ok(v) => v, diff --git a/src/command/types.rs b/src/command/types.rs index 8b64f46..9c5ab45 100644 --- a/src/command/types.rs +++ b/src/command/types.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use crate::view::Axis; use serde::{Deserialize, Serialize}; @@ -80,7 +82,7 @@ pub enum Command { /// Import a JSON file via the analyzer (non-interactive, uses auto-detected proposals). ImportJson { - path: String, + path: PathBuf, model_name: Option, /// Dot-path to the records array (empty = root) array_path: Option, diff --git a/src/import/csv_parser.rs b/src/import/csv_parser.rs index b05e8c5..2dd6f9f 100644 --- a/src/import/csv_parser.rs +++ b/src/import/csv_parser.rs @@ -1,15 +1,17 @@ +use std::path::Path; + use anyhow::{Context, Result}; use csv::ReaderBuilder; use serde_json::Value; /// Parse a CSV file and return records as serde_json::Value array -pub fn parse_csv(path: &str) -> Result> { +pub fn parse_csv(path: &Path) -> Result> { let mut reader = ReaderBuilder::new() .has_headers(true) .flexible(true) .trim(csv::Trim::All) .from_path(path) - .with_context(|| format!("Failed to open CSV file: {path}"))?; + .with_context(|| format!("Failed to open CSV file: {}", path.display()))?; // Detect if first row looks like headers (strings) or data (mixed) let has_headers = reader.headers().is_ok(); diff --git a/src/main.rs b/src/main.rs index 26f1270..f1dde67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use anyhow::{Context, Result}; use model::Model; use draw::run_tui; +use serde_json::Value; fn main() -> Result<()> { let args: Vec = std::env::args().collect(); @@ -35,42 +36,46 @@ impl Runnable for CmdLineArgs { let model = get_initial_model(&self.file_path)?; // Pre-TUI import: parse JSON or CSV and open wizard - let import_value = if let Some(ref path) = self.import_path { - match std::fs::read_to_string(path) { - Err(e) => { - eprintln!("Cannot read '{}': {e}", path.display()); - return Ok(()); - } - Ok(content) => { - if path.to_string_lossy().ends_with(".csv") { - // Parse CSV and wrap as JSON array - match crate::import::csv_parser::parse_csv(&path.to_string_lossy()) { - Ok(records) => Some(serde_json::Value::Array(records)), - Err(e) => { - eprintln!("CSV parse error: {e}"); - return Ok(()); - } - } - } else { - // Parse JSON - match serde_json::from_str::(&content) { - Err(e) => { - eprintln!("JSON parse error: {e}"); - return Ok(()); - } - Ok(json) => Some(json), - } - } - } - } - } else { - None - }; + let import_value = self.import_path.and_then(get_import_data); run_tui(model, self.file_path, import_value) } } +fn csv_path_p(path:&PathBuf) -> bool { + path.extension().is_some_and(|ext| ext.eq_ignore_ascii_case("csv")) +} + +fn get_import_data(path: PathBuf) -> Option { + match std::fs::read_to_string(&path) { + Err(e) => { + eprintln!("Cannot read '{}': {e}", path.display()); + None + } + Ok(content) => { + if csv_path_p(&path) { + // Parse CSV and wrap as JSON array + match crate::import::csv_parser::parse_csv(&path) { + Ok(records) => Some(serde_json::Value::Array(records)), + Err(e) => { + eprintln!("CSV parse error: {e}"); + None + } + } + } else { + // Parse JSON + match serde_json::from_str::(&content) { + Err(e) => { + eprintln!("JSON parse error: {e}"); + None + } + Ok(json) => Some(json), + } + } + } + } +} + struct HeadlessArgs { file_path: Option, commands: Vec,