1use crate::config::{FormatType, ReportType};
6use crate::kernel::{BalanceGroupSettings, RegisterSettings, Settings};
7use crate::model::TxnSet;
8use crate::tackler;
9pub use balance_group_reporter::BalanceGroupReporter;
10pub use balance_reporter::BalanceReporter;
11pub use register_reporter::RegisterReporter;
12use std::io;
13use std::io::Write;
14use std::path::{Path, PathBuf};
15use tackler_api::metadata::Metadata;
16use tackler_api::metadata::items::TimeZoneInfo;
17use tackler_rs::create_output_file;
18
19mod balance_group_reporter;
20mod balance_reporter;
21mod register_reporter;
22
23pub enum FormatWriter<'w> {
24 TxtFormat(Box<dyn io::Write + 'w>),
25 JsonFormat(Box<dyn io::Write + 'w>),
26}
27
28pub trait Report {
29 fn write_txt_report<'w, W: io::Write + ?Sized + 'w>(
30 &self,
31 cfg: &Settings,
32 w: &'w mut W,
33 txn_data: &TxnSet<'_>,
34 ) -> Result<(), tackler::Error> {
35 let mut writers: Vec<FormatWriter<'_>> = vec![FormatWriter::TxtFormat(Box::new(w))];
36 self.write_reports::<dyn io::Write>(cfg, &mut writers, None, txn_data)
37 }
38
39 fn write_reports<W: io::Write + ?Sized>(
40 &self,
41 cfg: &Settings,
42 writers: &mut Vec<FormatWriter<'_>>,
43 metadata: Option<&Metadata>,
44 txns: &TxnSet<'_>,
45 ) -> Result<(), tackler::Error>;
46}
47
48fn report_timezone(cfg: &Settings) -> Result<TimeZoneInfo, tackler::Error> {
49 Ok(TimeZoneInfo {
50 zone_id: match cfg.report.report_tz.iana_name() {
51 Some(tz) => tz.to_string(),
52 None => {
53 let msg = "no name for tz!?!";
54 return Err(msg.into());
55 }
56 },
57 })
58}
59
60fn report_output<W: io::Write + ?Sized>(
61 prog_writer: &mut Option<Box<W>>,
62 paths: Vec<(String, String)>,
63 title: &str,
64) -> Result<(), tackler::Error> {
65 if let Some(pw) = prog_writer.as_mut() {
66 for p in paths {
67 writeln!(pw, "{:>21} ({}): {}", title, p.0, p.1)?;
68 }
69 }
70 Ok(())
71}
72type ReportWriters<'w> = (Vec<FormatWriter<'w>>, Vec<(String, String)>);
73
74fn report_writers<'w>(
75 output_dir: &Path,
76 output_prefix: &str,
77 report_type: &ReportType,
78 settings: &Settings,
79) -> Result<ReportWriters<'w>, tackler::Error> {
80 match report_type {
81 ReportType::Balance => {
82 let mut writers = Vec::new();
83 let mut paths = Vec::new();
84
85 for rt in &settings.report.formats {
86 match rt {
87 FormatType::Txt => {
88 let (txt_writer, txt_path) =
89 create_output_file(output_dir, output_prefix, "bal", "txt")?;
90
91 writers.push(FormatWriter::TxtFormat(Box::new(txt_writer)));
92 paths.push(("TEXT".to_string(), txt_path));
93 }
94 FormatType::Json => {
95 let (json_writer, json_path) =
96 create_output_file(output_dir, output_prefix, "bal", "json")?;
97
98 writers.push(FormatWriter::JsonFormat(Box::new(json_writer)));
99 paths.push(("JSON".to_string(), json_path));
100 }
101 }
102 }
103 Ok((writers, paths))
104 }
105 ReportType::BalanceGroup => {
106 let mut writers = Vec::new();
107 let mut paths = Vec::new();
108
109 for rt in &settings.report.formats {
110 match rt {
111 FormatType::Txt => {
112 let (txt_writer, txt_path) =
113 create_output_file(output_dir, output_prefix, "balgrp", "txt")?;
114
115 writers.push(FormatWriter::TxtFormat(Box::new(txt_writer)));
116 paths.push(("TEXT".to_string(), txt_path));
117 }
118 FormatType::Json => {
119 let (json_writer, json_path) =
120 create_output_file(output_dir, output_prefix, "balgrp", "json")?;
121
122 writers.push(FormatWriter::JsonFormat(Box::new(json_writer)));
123 paths.push(("JSON".to_string(), json_path));
124 }
125 }
126 }
127 Ok((writers, paths))
128 }
129 ReportType::Register => {
130 let mut writers = Vec::new();
131 let mut paths = Vec::new();
132
133 for rt in &settings.report.formats {
134 match rt {
135 FormatType::Txt => {
136 let (txt_writer, txt_path) =
137 create_output_file(output_dir, output_prefix, "reg", "txt")?;
138
139 writers.push(FormatWriter::TxtFormat(Box::new(txt_writer)));
140 paths.push(("TEXT".to_string(), txt_path));
141 }
142 FormatType::Json => {
143 let (json_writer, json_path) =
144 create_output_file(output_dir, output_prefix, "reg", "json")?;
145
146 writers.push(FormatWriter::JsonFormat(Box::new(json_writer)));
147 paths.push(("JSON".to_string(), json_path));
148 }
149 }
150 }
151 Ok((writers, paths))
152 }
153 }
154}
155
156#[allow(clippy::too_many_arguments)]
157pub fn write_txt_reports<W: io::Write + ?Sized>(
158 console_writer: &mut Option<Box<W>>,
159 output_dir: Option<&PathBuf>,
160 output_prefix: &Option<String>,
161 reports: &Vec<ReportType>,
162 txn_set: &TxnSet<'_>,
163 settings: &Settings,
164 prog_writer: &mut Option<Box<W>>,
165) -> Result<(), tackler::Error> {
166 if !(output_dir.is_some() && output_prefix.is_some() && console_writer.is_none()
167 || output_dir.is_none() && output_prefix.is_none() && console_writer.is_some())
168 {
169 return Err("IE: Logic error, console output is not supported with file ouput".into());
170 }
171
172 let report_separator_len = 82;
173
174 let metadata = &txn_set
175 .metadata()
176 .map(|md| format!("{}\n", md.text(settings.report.report_tz.clone())))
177 .unwrap_or_default();
178
179 if let Some(cw) = console_writer.as_mut() {
180 write!(cw, "{}", metadata)?;
181 }
182
183 for r in reports {
184 match r {
185 ReportType::Balance => {
186 let bal_reporter = BalanceReporter::try_from(settings)?;
187
188 match (output_prefix, output_dir) {
189 (Some(output_name), Some(output_dir)) => {
190 let (mut writers, paths) =
191 report_writers(output_dir, output_name, r, settings)?;
192
193 bal_reporter.write_reports::<dyn io::Write>(
194 settings,
195 &mut writers,
196 txn_set.metadata(),
197 txn_set,
198 )?;
199
200 report_output(prog_writer, paths, "Balance Report")?;
201 }
202 _ => {
203 let mut cw = console_writer
204 .as_mut()
205 .expect("IE: logic error with output");
206
207 writeln!(cw, "{}", "*".repeat(report_separator_len))?;
208 bal_reporter.write_txt_report(settings, &mut cw, txn_set)?;
209 writeln!(cw, "{}", "#".repeat(report_separator_len))?;
210 }
211 }
212 }
213 ReportType::BalanceGroup => {
214 let bal_group_reporter = BalanceGroupReporter {
215 report_settings: BalanceGroupSettings::try_from(settings)?,
216 };
217 match (output_prefix, output_dir) {
218 (Some(output_name), Some(output_dir)) => {
219 let (mut writers, paths) =
220 report_writers(output_dir, output_name, r, settings)?;
221
222 bal_group_reporter.write_reports::<dyn io::Write>(
223 settings,
224 &mut writers,
225 txn_set.metadata(),
226 txn_set,
227 )?;
228 report_output(prog_writer, paths, "Balance Group Report")?;
229 }
230 _ => {
231 let mut cw = console_writer
232 .as_mut()
233 .expect("IE: logic error with output");
234
235 writeln!(cw, "{}", "*".repeat(report_separator_len))?;
236 bal_group_reporter.write_txt_report(settings, &mut cw, txn_set)?;
237 writeln!(cw, "{}", "#".repeat(report_separator_len))?;
238 }
239 }
240 }
241 ReportType::Register => {
242 let reg_reporter = RegisterReporter {
243 report_settings: RegisterSettings::try_from(settings)?,
244 };
245
246 match (output_prefix, output_dir) {
247 (Some(output_name), Some(output_dir)) => {
248 let (mut writers, paths) =
249 report_writers(output_dir, output_name, r, settings)?;
250
251 reg_reporter.write_reports::<dyn io::Write>(
252 settings,
253 &mut writers,
254 txn_set.metadata(),
255 txn_set,
256 )?;
257
258 report_output(prog_writer, paths, "Register Report")?;
259 }
260 _ => {
261 let mut cw = console_writer
262 .as_mut()
263 .expect("IE: logic error with output");
264
265 writeln!(cw, "{}", "*".repeat(report_separator_len))?;
266 reg_reporter.write_txt_report(settings, &mut cw, txn_set)?;
267 writeln!(cw, "{}", "#".repeat(report_separator_len))?;
268 }
269 }
270 }
271 }
272 }
273 Ok(())
274}