use crate::{
libs::{
config::Config,
export::{ExportData, ExportFormat, Exporter},
formatter::parse_date,
messages::Message,
},
msg_info,
};
use anyhow::Result;
use chrono::NaiveDate;
use clap::Args;
use std::path::PathBuf;
#[derive(Debug, Args)]
pub struct ExportArgs {
#[arg(value_enum, default_value = "report")]
data: ExportData,
#[arg(short, long, value_enum, default_value = "csv")]
format: ExportFormat,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(short, long, default_value = "today")]
date: String,
#[arg(long)]
hourly: bool,
}
pub async fn cmd(args: ExportArgs) -> Result<()> {
let date = parse_date(&args.date)?;
msg_info!(Message::ExportingData(format!("{:?}", args.data), format!("{:?}", args.format)));
let output = match args.output.clone() {
Some(path) => Some(path),
None => resolve_report_output(args.data, args.format, date)?,
};
let exporter = Exporter::new(args.format, output).hourly(args.hourly);
exporter.export(args.data, date).await?;
Ok(())
}
fn resolve_report_output(data: ExportData, format: ExportFormat, date: NaiveDate) -> Result<Option<PathBuf>> {
if !matches!(data, ExportData::Report) {
return Ok(None);
}
let report_config = match Config::read()?.report {
Some(config) => config,
None => return Ok(None),
};
let output_dir = match report_config.output_dir {
Some(dir) if !dir.trim().is_empty() => PathBuf::from(dir),
_ => return Ok(None),
};
std::fs::create_dir_all(&output_dir)?;
let template = report_config
.filename_template
.filter(|t| !t.trim().is_empty())
.unwrap_or_else(|| "daily_report_{date}{seq}".to_string());
let extension = match format {
ExportFormat::Csv => "csv",
ExportFormat::Json => "json",
ExportFormat::Excel => "xlsx",
};
let date_str = date.format("%Y-%m-%d").to_string();
for sequence in 1.. {
let seq_suffix = if sequence == 1 { String::new() } else { format!("_{}", sequence) };
let stem = template.replace("{date}", &date_str).replace("{seq}", &seq_suffix);
let candidate = output_dir.join(format!("{}.{}", stem, extension));
if !candidate.exists() {
return Ok(Some(candidate));
}
}
unreachable!("sequence iterator is unbounded")
}