Skip to main content

btctax_forms/
error.rs

1//! Error type for the PDF form-fill engine.
2
3/// Anything that can go wrong filling an official IRS PDF.
4///
5/// The **geometric read-back** failures (`Geometry`, `UnmappedField`) are the tax-safety net: a fill
6/// that lands a value in the wrong cell — or writes any field the map did not authorize — FAILS
7/// CLOSED (no PDF bytes are returned), so a mis-mapped form is never handed to a filer.
8#[derive(Debug, thiserror::Error)]
9pub enum FormsError {
10    /// The requested tax year has no bundled form set / map (this build ships TY2017, 2024 + 2025).
11    #[error("unsupported tax year {0}: this build bundles IRS forms for 2017, 2024 and 2025 only")]
12    UnsupportedYear(i32),
13
14    /// A field named by the map does not exist in the bundled PDF's AcroForm.
15    #[error("map references field {0:?} which is absent from the bundled PDF field set")]
16    MapFieldMissing(String),
17
18    /// More data rows than the form's page grid can hold on the paths that do not paginate.
19    #[error("{rows} rows exceed the {capacity}-row capacity of a single {part} page")]
20    Overflow {
21        /// The part being filled ("Part I" / "Part II").
22        part: &'static str,
23        /// The number of rows requested.
24        rows: usize,
25        /// The per-page row capacity from the map.
26        capacity: usize,
27    },
28
29    /// The geometric read-back found a written value in the WRONG column/row band — the map is
30    /// mis-aligned. Fails closed.
31    #[error("geometric read-back FAILED (mis-mapped cell): {0}")]
32    Geometry(String),
33
34    /// A field carries a value but the map never authorized writing it (a stray write). Fails closed.
35    #[error("read-back FAILED: unmapped field {0:?} was filled")]
36    UnmappedField(String),
37
38    /// A written value is longer than the cell's `/MaxLen` — the form declares a fixed-width (usually
39    /// **comb**) cell and the value does not fit. A PDF viewer would silently truncate it, or splay it
40    /// across the wrong comb teeth, so the fill fails closed instead: a truncated SSN on a filed return
41    /// is a wrong return.
42    #[error(
43        "read-back FAILED: {fqn:?} holds {len} characters but the cell's /MaxLen is {max_len}"
44    )]
45    CellOverflow {
46        /// The over-filled field.
47        fqn: String,
48        /// The cell's declared capacity.
49        max_len: usize,
50        /// The length actually written.
51        len: usize,
52    },
53
54    /// The bundled PDF's structure was not what the engine expects (missing AcroForm, bad Rect, …).
55    #[error("bundled PDF structure error: {0}")]
56    Structure(String),
57
58    /// Underlying lopdf parse/serialize error.
59    #[error("pdf error: {0}")]
60    Pdf(#[from] lopdf::Error),
61
62    /// A committed TOML map failed to parse.
63    #[error("map parse error: {0}")]
64    Map(#[from] toml::de::Error),
65
66    /// I/O error serializing the PDF.
67    #[error("io error: {0}")]
68    Io(#[from] std::io::Error),
69}