1use crate::error::EnvelopeResult;
6use crate::export::{csv, json, yaml};
7use crate::storage::Storage;
8use clap::{Subcommand, ValueEnum};
9use std::fs::File;
10use std::io::BufWriter;
11use std::path::PathBuf;
12
13#[derive(Debug, Clone, Copy, ValueEnum)]
15pub enum ExportFormat {
16 Csv,
18 Json,
20 Yaml,
22}
23
24#[derive(Subcommand, Debug)]
26pub enum ExportCommands {
27 All {
29 output: PathBuf,
31
32 #[arg(short, long, value_enum, default_value = "json")]
34 format: ExportFormat,
35
36 #[arg(long)]
38 pretty: bool,
39 },
40
41 Transactions {
43 output: PathBuf,
45 },
46
47 Allocations {
49 output: PathBuf,
51
52 #[arg(short, long, default_value = "12")]
54 months: usize,
55 },
56
57 Accounts {
59 output: PathBuf,
61 },
62
63 Info,
65}
66
67pub fn handle_export_command(storage: &Storage, cmd: ExportCommands) -> EnvelopeResult<()> {
69 match cmd {
70 ExportCommands::All {
71 output,
72 format,
73 pretty,
74 } => handle_export_all(storage, output, format, pretty),
75 ExportCommands::Transactions { output } => handle_export_transactions(storage, output),
76 ExportCommands::Allocations { output, months } => {
77 handle_export_allocations(storage, output, months)
78 }
79 ExportCommands::Accounts { output } => handle_export_accounts(storage, output),
80 ExportCommands::Info => handle_export_info(storage),
81 }
82}
83
84fn handle_export_all(
86 storage: &Storage,
87 output: PathBuf,
88 format: ExportFormat,
89 pretty: bool,
90) -> EnvelopeResult<()> {
91 let file = File::create(&output).map_err(|e| {
92 crate::error::EnvelopeError::Export(format!(
93 "Failed to create file {}: {}",
94 output.display(),
95 e
96 ))
97 })?;
98 let mut writer = BufWriter::new(file);
99
100 match format {
101 ExportFormat::Csv => {
102 csv::export_transactions_csv(storage, &mut writer)?;
104 println!("Transactions exported to: {}", output.display());
105 println!("Note: CSV format exports transactions only. Use JSON or YAML for full database export.");
106 }
107 ExportFormat::Json => {
108 json::export_full_json(storage, &mut writer, pretty)?;
109 println!("Full database exported to: {}", output.display());
110 }
111 ExportFormat::Yaml => {
112 yaml::export_full_yaml(storage, &mut writer)?;
113 println!("Full database exported to: {}", output.display());
114 }
115 }
116
117 Ok(())
118}
119
120fn handle_export_transactions(storage: &Storage, output: PathBuf) -> EnvelopeResult<()> {
122 let file = File::create(&output).map_err(|e| {
123 crate::error::EnvelopeError::Export(format!(
124 "Failed to create file {}: {}",
125 output.display(),
126 e
127 ))
128 })?;
129 let mut writer = BufWriter::new(file);
130
131 csv::export_transactions_csv(storage, &mut writer)?;
132
133 let count = storage.transactions.get_all()?.len();
134 println!("Exported {} transactions to: {}", count, output.display());
135
136 Ok(())
137}
138
139fn handle_export_allocations(
141 storage: &Storage,
142 output: PathBuf,
143 months: usize,
144) -> EnvelopeResult<()> {
145 use crate::models::BudgetPeriod;
146
147 let file = File::create(&output).map_err(|e| {
148 crate::error::EnvelopeError::Export(format!(
149 "Failed to create file {}: {}",
150 output.display(),
151 e
152 ))
153 })?;
154 let mut writer = BufWriter::new(file);
155
156 let current = BudgetPeriod::current_month();
158 let periods: Vec<_> = (0..months)
159 .map(|i| {
160 let mut p = current.clone();
161 for _ in 0..i {
162 p = p.prev();
163 }
164 p
165 })
166 .collect();
167
168 csv::export_allocations_csv(storage, &mut writer, Some(periods))?;
169
170 println!(
171 "Exported {} months of budget allocations to: {}",
172 months,
173 output.display()
174 );
175
176 Ok(())
177}
178
179fn handle_export_accounts(storage: &Storage, output: PathBuf) -> EnvelopeResult<()> {
181 let file = File::create(&output).map_err(|e| {
182 crate::error::EnvelopeError::Export(format!(
183 "Failed to create file {}: {}",
184 output.display(),
185 e
186 ))
187 })?;
188 let mut writer = BufWriter::new(file);
189
190 csv::export_accounts_csv(storage, &mut writer)?;
191
192 let count = storage.accounts.get_all()?.len();
193 println!("Exported {} accounts to: {}", count, output.display());
194
195 Ok(())
196}
197
198fn handle_export_info(storage: &Storage) -> EnvelopeResult<()> {
200 let export = json::FullExport::from_storage(storage)?;
201
202 println!("Export Information");
203 println!("==================\n");
204
205 println!("Schema Version: {}", export.schema_version);
206 println!("App Version: {}", export.app_version);
207 println!();
208
209 println!("Data Summary:");
210 println!(" Accounts: {}", export.metadata.account_count);
211 println!(" Transactions: {}", export.metadata.transaction_count);
212 println!(" Categories: {}", export.metadata.category_count);
213 println!(" Allocations: {}", export.metadata.allocation_count);
214 println!(" Payees: {}", export.metadata.payee_count);
215 println!();
216
217 if let Some(earliest) = &export.metadata.earliest_transaction {
218 println!("Transaction Date Range:");
219 println!(" Earliest: {}", earliest);
220 }
221 if let Some(latest) = &export.metadata.latest_transaction {
222 println!(" Latest: {}", latest);
223 }
224
225 println!("\nAvailable Export Formats:");
226 println!(" csv - CSV format (transactions, allocations, or accounts)");
227 println!(" json - JSON format (full database, machine-readable)");
228 println!(" yaml - YAML format (full database, human-readable)");
229
230 println!("\nExamples:");
231 println!(" envelope export all backup.json --format json --pretty");
232 println!(" envelope export transactions txns.csv");
233 println!(" envelope export accounts accounts.csv");
234
235 Ok(())
236}