use std::str::FromStr;
use clap::Parser;
use spreadsheet_to_json::heck::ToSnakeCase;
use spreadsheet_to_json::serde_json::{Number, Value};
use spreadsheet_to_json::FieldNameMode;
use spreadsheet_to_json::{is_truthy::*, options::{Column, OptionSet}, Format, ReadMode};
use simple_string_patterns::SimpleMatch;
use to_segments::ToSegments;
const DEFAULT_MAX_FOR_PREVIEW: u32 = 10;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
pub struct Args {
#[clap(short, long, value_parser) ]
pub sheet: Option<String>,
#[clap(short, long, value_parser, default_value_t = 0)]
pub index: u32,
pub path: Option<String>,
#[clap(long, value_parser, default_value_t = false)]
pub euro_number_format: bool,
#[clap(long, value_parser, default_value_t = false) ]
pub date_only: bool,
#[clap(short = 'k', long, value_parser) ]
pub keys: Option<String>,
#[clap(short, long, value_parser) ]
pub max: Option<u32>,
#[clap(short = 't', long, value_parser, default_value_t = 0) ]
pub header_row: u8,
#[clap(short = 'o',long, value_parser, default_value_t = false) ]
pub omit_header: bool,
#[clap(short = 'x',long, value_parser, default_value_t = false) ]
pub exclude_cells: bool,
#[clap(short = 'd',long, value_parser, default_value_t = false) ]
pub deferred: bool,
#[clap(short = 'p',long, value_parser, default_value_t = false) ]
pub preview: bool,
#[clap(short = 'l', long, value_parser, default_value_t = false) ]
pub lines: bool,
#[clap(short = 'r', long, value_parser, default_value_t = false) ]
pub rows: bool,
#[clap(long, value_parser, default_value_t = false) ]
pub debug: bool,
#[clap(short = 'c', long, value_parser) ]
pub colstyle: Option<String>,
#[clap(short = 'j', long, value_parser, default_value_t = false) ]
pub json: bool,
}
pub trait FromArgs {
fn from_args(args: &Args) -> Result<Self, String> where Self: Sized;
}
impl FromArgs for OptionSet {
fn from_args(args: &Args) -> Result<Self, String> {
let mut columns: Vec<Column> = vec![];
if let Some(k_string) = args.keys.clone() {
let split_parts = k_string.to_parts(",");
for ck in split_parts {
let pipe_parts = ck.to_parts("|");
let key_part = pipe_parts.first().cloned().unwrap_or_default();
let key_sub_parts = key_part.to_parts(":");
let source_key = key_sub_parts.first()
.map(|s| s.to_snake_case())
.filter(|s| !s.is_empty());
let Some(source_key) = source_key else {
continue;
};
let new_key = key_sub_parts.get(1)
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(|s| s.to_snake_case());
let fmt = pipe_parts.get(1)
.map(|s| Format::from_str(s).unwrap_or(Format::Auto))
.unwrap_or(Format::Auto);
let mut default_val = None;
if let Some(def_val) = pipe_parts.get(2).filter(|s| !s.is_empty()) {
default_val = match fmt {
Format::Integer => {
let parsed = i128::from_str(def_val).map_err(|_| {
format!("invalid --keys entry '{}': '{}' is not a valid integer default", ck, def_val)
})?;
let num = Number::from_i128(parsed).ok_or_else(|| {
format!("invalid --keys entry '{}': integer default '{}' is out of range", ck, def_val)
})?;
Some(Value::Number(num))
},
Format::Boolean => {
is_truthy_core(def_val, false).map(Value::Bool)
},
_ => Some(Value::String(def_val.clone()))
};
}
columns.push(Column::from_source_key_with_format(&source_key, new_key.as_deref(), fmt, default_val, false, false));
}
}
let read_mode = if args.preview {
ReadMode::PreviewMultiple
} else if args.deferred {
ReadMode::Async
} else {
ReadMode::Sync
};
let mut field_mode = FieldNameMode::AutoA1;
if let Some(colstyle) = args.colstyle.clone() {
let (col_key, col_mode) = colstyle.to_start_end(":");
if let Some(col_key) = col_key {
let override_all = col_mode.is_none_or(|m| m.starts_with_ci_alphanum("all"));
field_mode = FieldNameMode::from_key(col_key, override_all);
}
}
let jsonl = args.lines || read_mode.is_async();
let selected = args.sheet.clone().map(|sheet| sheet.to_segments(","));
let max = if args.preview {
Some(DEFAULT_MAX_FOR_PREVIEW)
} else {
args.max
};
Ok(OptionSet {
selected,
indices: vec![args.index],
path: args.path.clone(),
max,
header_row: args.header_row,
omit_header: args.omit_header,
rows: crate::RowOptionSet {
decimal_comma: args.euro_number_format,
date_only: args.date_only,
columns,
},
jsonl,
read_mode,
field_mode
})
}
}