use crate::filters::{FilterDefZoned, FilterDefinition};
use crate::metadata::Checksum;
use crate::txn_ts;
use jiff::Zoned;
use jiff::tz::TimeZone;
use serde::Serialize;
#[doc(hidden)]
pub type MetadataItems = Vec<MetadataItem>;
#[doc(hidden)]
pub trait Text: std::fmt::Debug {
#[must_use]
fn text(&self, tz: TimeZone) -> Vec<String>;
}
#[doc(hidden)]
#[derive(Serialize, Debug, Clone)]
pub enum MetadataItem {
#[doc(hidden)]
TxnSetChecksum(TxnSetChecksum),
#[doc(hidden)]
TimeZoneInfo(TimeZoneInfo),
#[doc(hidden)]
CreditAccountReport(CreditAccountReport),
#[doc(hidden)]
AccountSelectorChecksum(AccountSelectorChecksum),
#[doc(hidden)]
GitInputReference(GitInputReference),
#[doc(hidden)]
TxnFilterDescription(TxnFilterDescription),
#[doc(hidden)]
PriceRecords(PriceRecords),
}
impl MetadataItem {
pub const ITEM_PAD: usize = 15;
}
impl Text for MetadataItem {
fn text(&self, tz: TimeZone) -> Vec<String> {
match self {
Self::GitInputReference(gif) => gif.text(tz),
Self::TxnSetChecksum(tscs) => tscs.text(tz),
Self::TimeZoneInfo(tzinfo) => tzinfo.text(tz),
Self::CreditAccountReport(credit) => credit.text(tz),
Self::AccountSelectorChecksum(asc) => asc.text(tz),
Self::TxnFilterDescription(tfd) => tfd.text(tz),
Self::PriceRecords(pr) => pr.text(tz),
}
}
}
#[derive(Serialize, Debug, Clone)]
pub struct TxnSetChecksum {
pub size: usize,
pub hash: Checksum,
}
impl Text for TxnSetChecksum {
fn text(&self, _tz: TimeZone) -> Vec<String> {
let pad = MetadataItem::ITEM_PAD;
vec![
"Txn Set Checksum".to_string(),
format!("{:>pad$} : {}", self.hash.algorithm, &self.hash.value),
format!("{:>pad$} : {}", "set size", self.size),
]
}
}
#[derive(Serialize, Debug, Clone)]
pub struct AccountSelectorChecksum {
pub hash: Checksum,
pub selectors: Vec<String>,
}
impl Text for AccountSelectorChecksum {
fn text(&self, _tz: TimeZone) -> Vec<String> {
let pad = MetadataItem::ITEM_PAD;
let mut t = vec![
"Account Selector Checksum".to_string(),
format!("{:>pad$} : {}", self.hash.algorithm, &self.hash.value),
];
if !self.selectors.is_empty() {
let sel_txt = if self.selectors.len() > 1 {
"selectors"
} else {
"selector"
};
let l = format!(
"{:>pad$} : '{}'",
sel_txt,
&self.selectors.first().unwrap()
);
t.push(l);
for s in self.selectors.iter().skip(1) {
let l = format!("{:>pad$} | '{}'", "", s);
t.push(l);
}
}
t
}
}
#[derive(Serialize, Debug, Clone)]
pub struct CreditAccountReport {}
impl Text for CreditAccountReport {
fn text(&self, _tz: TimeZone) -> Vec<String> {
let pad = MetadataItem::ITEM_PAD;
vec![
"Credit Account Report".to_string(),
format!("{:>pad$} : {}", "NOTE", "All amounts are inverted"),
]
}
}
#[derive(Serialize, Debug, Clone)]
pub struct TimeZoneInfo {
#[serde(rename = "zoneId")]
pub zone_id: String,
}
impl Text for TimeZoneInfo {
fn text(&self, _tz: TimeZone) -> Vec<String> {
let pad = MetadataItem::ITEM_PAD;
vec![
"Report Time Zone".to_string(),
format!("{:>pad$} : {}", "TZ name", &self.zone_id),
]
}
}
#[derive(Serialize, Debug, Clone)]
pub struct TxnFilterDescription {
#[doc(hidden)]
#[serde(rename = "txnFilterDef")]
txn_filter_def: FilterDefinition,
}
impl TxnFilterDescription {
#[must_use]
pub fn from(tf: FilterDefinition) -> TxnFilterDescription {
TxnFilterDescription { txn_filter_def: tf }
}
}
impl Text for TxnFilterDescription {
fn text(&self, tz: TimeZone) -> Vec<String> {
format!(
"{}",
FilterDefZoned {
filt_def: &self.txn_filter_def,
tz
}
)
.trim_end()
.split('\n')
.map(String::from)
.collect::<Vec<String>>()
}
}
#[derive(Serialize, Debug, Clone)]
pub struct GitInputReference {
pub commit: String,
#[serde(rename = "ref")]
#[serde(skip_serializing_if = "Option::is_none")]
pub reference: Option<String>,
pub dir: String,
pub extension: String,
pub author: String,
pub date: String,
pub subject: String,
}
impl Text for GitInputReference {
fn text(&self, _tz: TimeZone) -> Vec<String> {
let pad = MetadataItem::ITEM_PAD;
vec![
"Git Storage".to_string(),
format!(
"{:>pad$} : {}",
"reference",
self.reference
.as_ref()
.unwrap_or(&"FIXED by commit".to_string())
),
format!("{:>pad$} : {}", "directory", self.dir),
format!("{:>pad$} : {}", "extension", self.extension),
format!("{:>pad$} : {}", "commit", self.commit),
format!("{:>pad$} : {}", "author", self.author),
format!("{:>pad$} : {}", "date", self.date),
format!("{:>pad$} : {}", "subject", self.subject),
]
}
}
#[derive(Serialize, Debug, Clone)]
pub struct PriceRecord {
#[serde(skip_serializing_if = "Option::is_none")]
pub ts: Option<Zoned>,
pub source: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate: Option<String>,
pub target: String,
}
impl Text for PriceRecord {
fn text(&self, tz: TimeZone) -> Vec<String> {
let pad = MetadataItem::ITEM_PAD;
vec![
format!(
"{:>pad$} : {}",
"Time",
self.ts.as_ref().map_or("At txn time".to_string(), |ts| {
txn_ts::as_tz_full(ts, tz)
})
),
format!("{:>pad$} : {}", "Commodity", self.source),
format!(
"{:>pad$} : {} {}",
"Value",
self.rate.clone().unwrap_or("-".to_string()),
self.target
),
]
}
}
#[derive(Serialize, Debug, Clone)]
pub struct PriceRecords {
pub rates: Vec<PriceRecord>,
}
impl Text for PriceRecords {
fn text(&self, tz: TimeZone) -> Vec<String> {
let pad = MetadataItem::ITEM_PAD;
let mut txt = Vec::new();
if let Some(pr) = self.rates.first() {
txt.push("Commodity Prices".to_string());
txt.extend(pr.text(tz.clone()));
if self.rates.len() > 1 {
for pr in &self.rates[1..] {
txt.push(format!("{:>pad$} -", ""));
txt.extend(pr.text(tz.clone()));
}
}
}
txt
}
}