1use crate::filters::{FilterDefZoned, FilterDefinition};
10use crate::metadata::Checksum;
11use crate::txn_ts;
12use jiff::Zoned;
13use jiff::tz::TimeZone;
14use serde::Serialize;
15
16#[doc(hidden)]
17pub type MetadataItems = Vec<MetadataItem>;
18
19#[doc(hidden)]
20pub trait Text: std::fmt::Debug {
21 #[must_use]
23 fn text(&self, tz: TimeZone) -> Vec<String>;
24}
25
26#[doc(hidden)]
27#[derive(Serialize, Debug, Clone)]
28pub enum MetadataItem {
29 #[doc(hidden)]
30 TxnSetChecksum(TxnSetChecksum),
31 #[doc(hidden)]
32 TimeZoneInfo(TimeZoneInfo),
33 #[doc(hidden)]
34 CreditAccountReport(CreditAccountReport),
35 #[doc(hidden)]
36 AccountSelectorChecksum(AccountSelectorChecksum),
37 #[doc(hidden)]
38 GitInputReference(GitInputReference),
39 #[doc(hidden)]
40 TxnFilterDescription(TxnFilterDescription),
41 #[doc(hidden)]
42 PriceRecords(PriceRecords),
43}
44
45impl MetadataItem {
46 pub const ITEM_PAD: usize = 15;
47}
48
49impl Text for MetadataItem {
50 fn text(&self, tz: TimeZone) -> Vec<String> {
51 match self {
52 Self::GitInputReference(gif) => gif.text(tz),
53 Self::TxnSetChecksum(tscs) => tscs.text(tz),
54 Self::TimeZoneInfo(tzinfo) => tzinfo.text(tz),
55 Self::CreditAccountReport(credit) => credit.text(tz),
56 Self::AccountSelectorChecksum(asc) => asc.text(tz),
57 Self::TxnFilterDescription(tfd) => tfd.text(tz),
58 Self::PriceRecords(pr) => pr.text(tz),
59 }
60 }
61}
62
63#[derive(Serialize, Debug, Clone)]
65pub struct TxnSetChecksum {
66 pub size: usize,
68 pub hash: Checksum,
70}
71impl Text for TxnSetChecksum {
72 fn text(&self, _tz: TimeZone) -> Vec<String> {
73 let pad = MetadataItem::ITEM_PAD;
75 vec![
76 format!("Txn Set Checksum"),
77 format!("{:>pad$} : {}", self.hash.algorithm, &self.hash.value),
78 format!("{:>pad$} : {}", "set size", self.size),
79 ]
80 }
81}
82
83#[derive(Serialize, Debug, Clone)]
96pub struct AccountSelectorChecksum {
97 pub hash: Checksum,
99 pub selectors: Vec<String>,
101}
102impl Text for AccountSelectorChecksum {
103 fn text(&self, _tz: TimeZone) -> Vec<String> {
104 let pad = MetadataItem::ITEM_PAD;
106 let mut t = vec![
107 format!("Account Selector Checksum"),
108 format!("{:>pad$} : {}", self.hash.algorithm, &self.hash.value),
109 ];
110 if !self.selectors.is_empty() {
111 let sel_txt = if self.selectors.len() > 1 {
112 "selectors"
113 } else {
114 "selector"
115 };
116 let l = format!(
117 "{:>pad$} : '{}'",
118 sel_txt,
119 &self.selectors.first().unwrap()
120 );
121 t.push(l);
122 for s in self.selectors.iter().skip(1) {
123 let l = format!("{:>pad$} | '{}'", "", s);
124 t.push(l);
125 }
126 }
127 t
128 }
129}
130
131#[derive(Serialize, Debug, Clone)]
135pub struct CreditAccountReport {}
136
137impl Text for CreditAccountReport {
138 fn text(&self, _tz: TimeZone) -> Vec<String> {
139 let pad = MetadataItem::ITEM_PAD;
140 vec![
141 "Credit Account Report".to_string(),
142 format!("{:>pad$} : {}", "NOTE", "All amounts are inverted"),
143 ]
144 }
145}
146
147#[derive(Serialize, Debug, Clone)]
149pub struct TimeZoneInfo {
150 #[serde(rename = "zoneId")]
152 pub zone_id: String,
153}
154impl Text for TimeZoneInfo {
155 fn text(&self, _tz: TimeZone) -> Vec<String> {
156 let pad = MetadataItem::ITEM_PAD;
157 vec![
158 "Report Time Zone".to_string(),
159 format!("{:>pad$} : {}", "TZ name", &self.zone_id),
160 ]
161 }
162}
163#[derive(Serialize, Debug, Clone)]
166pub struct TxnFilterDescription {
167 #[doc(hidden)]
168 #[serde(rename = "txnFilterDef")]
169 txn_filter_def: FilterDefinition,
170}
171
172impl TxnFilterDescription {
173 #[must_use]
176 pub fn from(tf: FilterDefinition) -> TxnFilterDescription {
177 TxnFilterDescription { txn_filter_def: tf }
178 }
179}
180impl Text for TxnFilterDescription {
181 fn text(&self, tz: TimeZone) -> Vec<String> {
182 format!(
185 "{}",
186 FilterDefZoned {
187 filt_def: &self.txn_filter_def,
188 tz
189 }
190 )
191 .trim_end()
192 .split('\n')
193 .map(String::from)
194 .collect::<Vec<String>>()
195 }
196}
197
198#[derive(Serialize, Debug, Clone)]
201pub struct GitInputReference {
202 pub commit: String,
204
205 #[serde(rename = "ref")]
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub reference: Option<String>,
209
210 pub dir: String,
212
213 pub extension: String,
215
216 pub author: String,
218
219 pub date: String,
221
222 pub subject: String,
224}
225
226impl Text for GitInputReference {
227 fn text(&self, _tz: TimeZone) -> Vec<String> {
228 let pad = MetadataItem::ITEM_PAD;
229 vec![
230 format!("Git Storage"),
231 format!(
232 "{:>pad$} : {}",
233 "reference",
234 self.reference
235 .as_ref()
236 .unwrap_or(&"FIXED by commit".to_string())
237 ),
238 format!("{:>pad$} : {}", "directory", self.dir),
239 format!("{:>pad$} : {}", "extension", self.extension),
240 format!("{:>pad$} : {}", "commit", self.commit),
241 format!("{:>pad$} : {}", "author", self.author),
242 format!("{:>pad$} : {}", "date", self.date),
243 format!("{:>pad$} : {}", "subject", self.subject),
244 ]
245 }
246}
247
248#[derive(Serialize, Debug, Clone)]
250pub struct PriceRecord {
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub ts: Option<Zoned>,
254 pub source: String,
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub rate: Option<String>,
259 pub target: String,
261}
262impl Text for PriceRecord {
263 fn text(&self, tz: TimeZone) -> Vec<String> {
264 let pad = MetadataItem::ITEM_PAD;
265 vec![
266 format!(
267 "{:>pad$} : {}",
268 "Time",
269 self.ts.as_ref().map_or("At txn time".to_string(), |ts| {
270 txn_ts::as_tz_full(ts, tz)
271 })
272 ),
273 format!("{:>pad$} : {}", "Commodity", self.source),
274 format!(
275 "{:>pad$} : {} {}",
276 "Value",
277 self.rate.clone().map_or("-".to_string(), |v| v),
278 self.target
279 ),
280 ]
281 }
282}
283#[derive(Serialize, Debug, Clone)]
285pub struct PriceRecords {
286 pub rates: Vec<PriceRecord>,
288}
289impl Text for PriceRecords {
290 fn text(&self, tz: TimeZone) -> Vec<String> {
291 let pad = MetadataItem::ITEM_PAD;
292
293 let mut txt = Vec::new();
294
295 if let Some(pr) = self.rates.first() {
296 txt.push("Commodity Prices".to_string());
297 txt.extend(pr.text(tz.clone()));
298
299 if self.rates.len() > 1 {
300 for pr in &self.rates[1..] {
301 txt.push(format!("{:>pad$} -", ""));
302 txt.extend(pr.text(tz.clone()));
303 }
304 }
305 }
306 txt
307 }
308}