use crate::error::FormsError;
use crate::fmt_money;
use crate::map::{IdentityCells, MoneyCell};
use crate::pdf::{Field, FieldValue};
use crate::verify::FlatPlacement;
use btctax_core::tax::packet::Ssn;
use btctax_core::Usd;
use rust_decimal_macros::dec;
pub fn page_of(fqn: &str) -> usize {
if fqn.contains("Page2") {
1
} else {
0
}
}
pub fn fmt_money_pair(d: Usd) -> (String, String) {
let r = d.round_dp(2);
let whole = r.trunc();
let cents = ((r - whole).abs() * dec!(100)).round();
(whole.trunc().to_string(), format!("{:02}", cents))
}
pub fn push_money(
w: &mut Vec<(String, FieldValue)>,
p: &mut Vec<FlatPlacement>,
cell: &MoneyCell,
value: Usd,
col: usize,
descent: Option<(u32, u32)>,
) {
match cell {
MoneyCell::Single(fqn) => {
w.push((fqn.clone(), FieldValue::Text(fmt_money(value))));
p.push(geo_placement(fqn, col, descent));
}
MoneyCell::Pair(mp) => {
let (dollars, cents) = fmt_money_pair(value);
w.push((mp.dollars_field.clone(), FieldValue::Text(dollars)));
w.push((mp.cents_field.clone(), FieldValue::Text(cents)));
p.push(geo_placement(&mp.dollars_field, col, descent));
p.push(FlatPlacement::free(
mp.cents_field.clone(),
page_of(&mp.cents_field),
));
}
}
}
pub fn push_literal(
w: &mut Vec<(String, FieldValue)>,
p: &mut Vec<FlatPlacement>,
cell: &MoneyCell,
literal: &str,
col: usize,
) {
let fqn = match cell {
MoneyCell::Single(f) => f,
MoneyCell::Pair(mp) => &mp.dollars_field,
};
w.push((fqn.clone(), FieldValue::Text(literal.to_string())));
p.push(FlatPlacement::col_only(fqn.clone(), page_of(fqn), col));
}
fn geo_placement(fqn: &str, col: usize, descent: Option<(u32, u32)>) -> FlatPlacement {
let page = page_of(fqn);
match descent {
Some((grp, ord)) => FlatPlacement::cell(fqn.to_string(), page, col, grp, ord),
None => FlatPlacement::col_only(fqn.to_string(), page, col),
}
}
pub fn render_ssn(ssn: &Ssn, max_len: Option<usize>) -> Result<String, FormsError> {
match max_len {
None => Ok(ssn.hyphenated()),
Some(n) if n >= 11 => Ok(ssn.hyphenated()),
Some(n) if n >= 9 => Ok(ssn.digits().to_string()),
Some(n) => Err(FormsError::Geometry(format!(
"an SSN cell with /MaxLen {n} cannot hold an SSN (9 digits) — the map points at the wrong widget"
))),
}
}
pub fn push_identity(
w: &mut Vec<(String, FieldValue)>,
p: &mut Vec<FlatPlacement>,
cells: &IdentityCells,
name: &str,
ssn: &Ssn,
fields: &[Field],
) -> Result<(), FormsError> {
let ssn_max_len = fields
.iter()
.find(|f| f.fqn == cells.ssn)
.ok_or_else(|| FormsError::MapFieldMissing(cells.ssn.clone()))?
.max_len;
w.push((cells.name.clone(), FieldValue::Text(name.to_string())));
p.push(FlatPlacement::free(
cells.name.clone(),
page_of(&cells.name),
));
w.push((
cells.ssn.clone(),
FieldValue::Text(render_ssn(ssn, ssn_max_len)?),
));
p.push(FlatPlacement::free(cells.ssn.clone(), page_of(&cells.ssn)));
Ok(())
}