mod cells;
mod error;
mod fill8949;
mod form1040;
mod form8283;
mod map;
mod overflow;
mod pdf;
mod schedule_d;
mod schedule_se;
mod verify;
mod watermark;
pub use error::FormsError;
pub use form1040::{Form1040Fill, Form1040Inputs};
pub use map::{Form1040Map, Form8283Map, Form8949Map, ScheduleDMap, ScheduleSeMap};
pub use schedule_se::SE_FLOOR;
use btctax_core::conventions::{TaxDate, Usd};
use btctax_core::{Form8949Row, ScheduleDTotals};
use time::macros::format_description;
pub const SUPPORTED_YEARS: &[i32] = &[2017, 2024, 2025];
pub(crate) fn fmt_date(d: TaxDate) -> Result<String, FormsError> {
let fmt = format_description!("[month]/[day]/[year]");
d.format(&fmt)
.map_err(|e| FormsError::Structure(format!("date format: {e}")))
}
pub(crate) fn fmt_money(d: Usd) -> String {
d.to_string()
}
pub fn fill_form_8949(rows: &[Form8949Row], year: i32) -> Result<Vec<u8>, FormsError> {
let map = Form8949Map::for_year(year)?;
let cap = map.rows_per_page;
let (st, lt) = fill8949::split_parts(rows);
let n_pages = div_ceil(st.len(), cap).max(div_ceil(lt.len(), cap)).max(1);
if n_pages == 1 {
let short = fill8949::part_data(&st)?;
let long = fill8949::part_data(<)?;
return fill8949::fill_8949_parts(&short, &long, &map);
}
let mut copies = Vec::with_capacity(n_pages);
for k in 0..n_pages {
let st_chunk: Vec<&Form8949Row> = st.iter().skip(k * cap).take(cap).copied().collect();
let lt_chunk: Vec<&Form8949Row> = lt.iter().skip(k * cap).take(cap).copied().collect();
let short = fill8949::part_data(&st_chunk)?;
let long = fill8949::part_data(<_chunk)?;
copies.push(fill8949::fill_8949_parts(&short, &long, &map)?);
}
overflow::merge_copies(&copies)
}
fn div_ceil(n: usize, d: usize) -> usize {
n.div_ceil(d)
}
pub fn stamp_draft_watermark(pdf_bytes: &[u8]) -> Result<Vec<u8>, FormsError> {
watermark::stamp_draft(pdf_bytes)
}
pub fn fill_schedule_d(totals: &ScheduleDTotals, year: i32) -> Result<Vec<u8>, FormsError> {
let map = ScheduleDMap::for_year(year)?;
schedule_d::fill_schedule_d_totals(totals, &map)
}
pub fn fill_schedule_se(
se: &btctax_core::SeTaxResult,
w2_ss_wages: Usd,
ss_wage_base: Usd,
year: i32,
) -> Result<Option<Vec<u8>>, FormsError> {
let map = ScheduleSeMap::for_year(year)?;
schedule_se::fill_schedule_se_with_map(se, w2_ss_wages, ss_wage_base, &map)
}
pub fn fill_form_8283(
rows: &[btctax_core::Form8283Row],
year: i32,
) -> Result<Option<Vec<u8>>, FormsError> {
let map = Form8283Map::for_year(year)?;
form8283::fill_form_8283(rows, &map)
}
pub fn fill_form_1040_capgains(
inputs: &Form1040Inputs,
year: i32,
) -> Result<Option<Form1040Fill>, FormsError> {
let map = Form1040Map::for_year(year)?;
form1040::fill_form_1040_capgains(inputs, &map)
}
pub fn rows_possibly_broker_reported(rows: &[Form8949Row]) -> usize {
rows.iter().filter(|r| r.box_needs_review).count()
}
#[doc(hidden)]
pub mod testonly {
pub use crate::cells::fmt_money_pair;
pub use crate::fill8949::{fill_8949_parts, part_data, pdf_has_xfa, split_parts, PartData};
pub use crate::form1040::{fill_form_1040_capgains as fill_1040_with_map, Form1040Fill};
pub use crate::form8283::fill_form_8283 as fill_8283_with_map;
pub use crate::map::{
AmountCols, Form1040Map, Form8283Map, Form8949Map, MoneyCell, MoneyPair, PartMap,
ScheduleDMap, ScheduleSeMap,
};
pub use crate::pdf::{
button_on_states, checkbox_on, collect_fields, index, load, text_value, Field,
F1040_PDF_2017, F1040_PDF_2024, F1040_PDF_2025, F8283_PDF_2017, F8283_PDF_2024,
F8283_PDF_2025, F8949_PDF_2017, F8949_PDF_2024, F8949_PDF_2025, SCHEDULE_D_PDF_2017,
SCHEDULE_D_PDF_2024, SCHEDULE_D_PDF_2025, SCHEDULE_SE_PDF_2017, SCHEDULE_SE_PDF_2024,
SCHEDULE_SE_PDF_2025,
};
pub use crate::schedule_d::fill_schedule_d_totals;
pub use crate::schedule_se::fill_schedule_se_with_map;
pub use crate::verify::{
no_unmapped_filled, topmost_yes_no_pair, verify_8949, verify_flat, FlatPlacement, Geo,
Placement,
};
}