use crate::error::EnvelopeResult;
use crate::export::{csv, json, yaml};
use crate::storage::Storage;
use clap::{Subcommand, ValueEnum};
use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum ExportFormat {
Csv,
Json,
Yaml,
}
#[derive(Subcommand, Debug)]
pub enum ExportCommands {
All {
output: PathBuf,
#[arg(short, long, value_enum, default_value = "json")]
format: ExportFormat,
#[arg(long)]
pretty: bool,
},
Transactions {
output: PathBuf,
},
Allocations {
output: PathBuf,
#[arg(short, long, default_value = "12")]
months: usize,
},
Accounts {
output: PathBuf,
},
Info,
}
pub fn handle_export_command(storage: &Storage, cmd: ExportCommands) -> EnvelopeResult<()> {
match cmd {
ExportCommands::All {
output,
format,
pretty,
} => handle_export_all(storage, output, format, pretty),
ExportCommands::Transactions { output } => handle_export_transactions(storage, output),
ExportCommands::Allocations { output, months } => {
handle_export_allocations(storage, output, months)
}
ExportCommands::Accounts { output } => handle_export_accounts(storage, output),
ExportCommands::Info => handle_export_info(storage),
}
}
fn handle_export_all(
storage: &Storage,
output: PathBuf,
format: ExportFormat,
pretty: bool,
) -> EnvelopeResult<()> {
let file = File::create(&output).map_err(|e| {
crate::error::EnvelopeError::Export(format!(
"Failed to create file {}: {}",
output.display(),
e
))
})?;
let mut writer = BufWriter::new(file);
match format {
ExportFormat::Csv => {
csv::export_transactions_csv(storage, &mut writer)?;
println!("Transactions exported to: {}", output.display());
println!("Note: CSV format exports transactions only. Use JSON or YAML for full database export.");
}
ExportFormat::Json => {
json::export_full_json(storage, &mut writer, pretty)?;
println!("Full database exported to: {}", output.display());
}
ExportFormat::Yaml => {
yaml::export_full_yaml(storage, &mut writer)?;
println!("Full database exported to: {}", output.display());
}
}
Ok(())
}
fn handle_export_transactions(storage: &Storage, output: PathBuf) -> EnvelopeResult<()> {
let file = File::create(&output).map_err(|e| {
crate::error::EnvelopeError::Export(format!(
"Failed to create file {}: {}",
output.display(),
e
))
})?;
let mut writer = BufWriter::new(file);
csv::export_transactions_csv(storage, &mut writer)?;
let count = storage.transactions.get_all()?.len();
println!("Exported {} transactions to: {}", count, output.display());
Ok(())
}
fn handle_export_allocations(
storage: &Storage,
output: PathBuf,
months: usize,
) -> EnvelopeResult<()> {
use crate::models::BudgetPeriod;
let file = File::create(&output).map_err(|e| {
crate::error::EnvelopeError::Export(format!(
"Failed to create file {}: {}",
output.display(),
e
))
})?;
let mut writer = BufWriter::new(file);
let current = BudgetPeriod::current_month();
let periods: Vec<_> = (0..months)
.map(|i| {
let mut p = current.clone();
for _ in 0..i {
p = p.prev();
}
p
})
.collect();
csv::export_allocations_csv(storage, &mut writer, Some(periods))?;
println!(
"Exported {} months of budget allocations to: {}",
months,
output.display()
);
Ok(())
}
fn handle_export_accounts(storage: &Storage, output: PathBuf) -> EnvelopeResult<()> {
let file = File::create(&output).map_err(|e| {
crate::error::EnvelopeError::Export(format!(
"Failed to create file {}: {}",
output.display(),
e
))
})?;
let mut writer = BufWriter::new(file);
csv::export_accounts_csv(storage, &mut writer)?;
let count = storage.accounts.get_all()?.len();
println!("Exported {} accounts to: {}", count, output.display());
Ok(())
}
fn handle_export_info(storage: &Storage) -> EnvelopeResult<()> {
let export = json::FullExport::from_storage(storage)?;
println!("Export Information");
println!("==================\n");
println!("Schema Version: {}", export.schema_version);
println!("App Version: {}", export.app_version);
println!();
println!("Data Summary:");
println!(" Accounts: {}", export.metadata.account_count);
println!(" Transactions: {}", export.metadata.transaction_count);
println!(" Categories: {}", export.metadata.category_count);
println!(" Allocations: {}", export.metadata.allocation_count);
println!(" Payees: {}", export.metadata.payee_count);
println!();
if let Some(earliest) = &export.metadata.earliest_transaction {
println!("Transaction Date Range:");
println!(" Earliest: {}", earliest);
}
if let Some(latest) = &export.metadata.latest_transaction {
println!(" Latest: {}", latest);
}
println!("\nAvailable Export Formats:");
println!(" csv - CSV format (transactions, allocations, or accounts)");
println!(" json - JSON format (full database, machine-readable)");
println!(" yaml - YAML format (full database, human-readable)");
println!("\nExamples:");
println!(" envelope export all backup.json --format json --pretty");
println!(" envelope export transactions txns.csv");
println!(" envelope export accounts accounts.csv");
Ok(())
}