use crate::error::FormsError;
use serde::Deserialize;
pub const F8949_MAP_2025: &str = include_str!("../forms/2025/f8949.map.toml");
pub const SCHEDULE_D_MAP_2025: &str = include_str!("../forms/2025/schedule_d.map.toml");
pub const SCHEDULE_SE_MAP_2025: &str = include_str!("../forms/2025/schedule_se.map.toml");
pub const F8283_MAP_2025: &str = include_str!("../forms/2025/f8283.map.toml");
pub const F1040_MAP_2025: &str = include_str!("../forms/2025/f1040.map.toml");
pub const F8949_MAP_2024: &str = include_str!("../forms/2024/f8949.map.toml");
pub const SCHEDULE_D_MAP_2024: &str = include_str!("../forms/2024/schedule_d.map.toml");
pub const SCHEDULE_SE_MAP_2024: &str = include_str!("../forms/2024/schedule_se.map.toml");
pub const F8283_MAP_2024: &str = include_str!("../forms/2024/f8283.map.toml");
pub const F1040_MAP_2024: &str = include_str!("../forms/2024/f1040.map.toml");
pub const F8949_MAP_2017: &str = include_str!("../forms/2017/f8949.map.toml");
pub const SCHEDULE_D_MAP_2017: &str = include_str!("../forms/2017/schedule_d.map.toml");
pub const SCHEDULE_SE_MAP_2017: &str = include_str!("../forms/2017/schedule_se.map.toml");
pub const F8283_MAP_2017: &str = include_str!("../forms/2017/f8283.map.toml");
pub const F1040_MAP_2017: &str = include_str!("../forms/2017/f1040.map.toml");
#[derive(Debug, Clone, Deserialize)]
pub struct AmountCols {
pub proceeds_d: String,
pub cost_e: String,
pub adj_g: String,
pub gain_h: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PartMap {
pub term: String,
pub page: usize,
pub box_field: String,
pub box_on: String,
pub totals: AmountCols,
pub rows: Vec<Vec<String>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Form8949Map {
pub form: String,
pub year: i32,
pub rows_per_page: usize,
pub table_token: String,
pub parts: Vec<PartMap>,
}
impl Form8949Map {
pub fn parse(toml_src: &str) -> Result<Self, toml::de::Error> {
toml::from_str(toml_src)
}
pub fn ty2025() -> Self {
Self::parse(F8949_MAP_2025).expect("bundled f8949 2025 map parses")
}
pub fn ty2024() -> Self {
Self::parse(F8949_MAP_2024).expect("bundled f8949 2024 map parses")
}
pub fn ty2017() -> Self {
Self::parse(F8949_MAP_2017).expect("bundled f8949 2017 map parses")
}
pub fn for_year(year: i32) -> Result<Self, FormsError> {
match year {
2017 => Ok(Self::ty2017()),
2024 => Ok(Self::ty2024()),
2025 => Ok(Self::ty2025()),
_ => Err(FormsError::UnsupportedYear(year)),
}
}
pub fn part(&self, term: &str) -> Option<&PartMap> {
self.parts.iter().find(|p| p.term == term)
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct CheckChoice {
pub field: String,
pub on: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MoneyPair {
pub dollars_field: String,
pub cents_field: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum MoneyCell {
Single(String),
Pair(MoneyPair),
}
impl MoneyCell {
pub fn fields(&self) -> Vec<&str> {
match self {
MoneyCell::Single(f) => vec![f.as_str()],
MoneyCell::Pair(p) => vec![p.dollars_field.as_str(), p.cents_field.as_str()],
}
}
}
fn default_da_present() -> bool {
true
}
#[derive(Debug, Clone, Deserialize)]
pub struct Form1040Map {
pub form: String,
pub year: i32,
pub line7a: MoneyCell,
#[serde(default = "default_da_present")]
pub da_present: bool,
#[serde(default)]
pub da_yes: Option<CheckChoice>,
#[serde(default)]
pub da_no: Option<CheckChoice>,
}
impl Form1040Map {
pub fn parse(toml_src: &str) -> Result<Self, toml::de::Error> {
toml::from_str(toml_src)
}
pub fn ty2025() -> Self {
Self::parse(F1040_MAP_2025).expect("bundled f1040 2025 map parses")
}
pub fn ty2024() -> Self {
Self::parse(F1040_MAP_2024).expect("bundled f1040 2024 map parses")
}
pub fn ty2017() -> Self {
Self::parse(F1040_MAP_2017).expect("bundled f1040 2017 map parses")
}
pub fn for_year(year: i32) -> Result<Self, FormsError> {
match year {
2017 => Ok(Self::ty2017()),
2024 => Ok(Self::ty2024()),
2025 => Ok(Self::ty2025()),
_ => Err(FormsError::UnsupportedYear(year)),
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Section8283ARow {
pub donee: String,
pub desc: String,
pub date_contrib: String,
pub date_acq: String,
pub how: String,
pub cost: MoneyCell,
pub fmv: MoneyCell,
pub method: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Section8283A {
pub rows: Vec<Section8283ARow>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Section8283BRow {
pub desc: String,
pub fmv: MoneyCell,
pub date_acq: String,
pub how: String,
pub cost: MoneyCell,
pub deduction: MoneyCell,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Section8283B {
pub k_digital_assets: CheckChoice,
#[serde(default)]
pub btc_property_note: Option<String>,
#[serde(default)]
pub appraiser_name: Option<String>,
pub appraiser_address: String,
pub appraiser_tin: String,
pub donee_name: String,
pub donee_ein: String,
pub donee_address: String,
pub rows: Vec<Section8283BRow>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Form8283Map {
pub form: String,
pub year: i32,
pub section_a: Section8283A,
pub section_b: Section8283B,
}
impl Form8283Map {
pub fn parse(toml_src: &str) -> Result<Self, toml::de::Error> {
toml::from_str(toml_src)
}
pub fn ty2025() -> Self {
Self::parse(F8283_MAP_2025).expect("bundled f8283 2025 map parses")
}
pub fn ty2024() -> Self {
Self::parse(F8283_MAP_2024).expect("bundled f8283 2024 map parses")
}
pub fn ty2017() -> Self {
Self::parse(F8283_MAP_2017).expect("bundled f8283 2017 map parses")
}
pub fn for_year(year: i32) -> Result<Self, FormsError> {
match year {
2017 => Ok(Self::ty2017()),
2024 => Ok(Self::ty2024()),
2025 => Ok(Self::ty2025()),
_ => Err(FormsError::UnsupportedYear(year)),
}
}
pub fn field_names(&self) -> Vec<&str> {
let mut v = Vec::new();
for r in &self.section_a.rows {
v.extend([
r.donee.as_str(),
r.desc.as_str(),
r.date_contrib.as_str(),
r.date_acq.as_str(),
r.how.as_str(),
]);
v.extend(r.cost.fields());
v.extend(r.fmv.fields());
v.push(r.method.as_str());
}
let b = &self.section_b;
v.push(b.k_digital_assets.field.as_str());
if let Some(n) = &b.appraiser_name {
v.push(n.as_str());
}
v.extend([
b.appraiser_address.as_str(),
b.appraiser_tin.as_str(),
b.donee_name.as_str(),
b.donee_ein.as_str(),
b.donee_address.as_str(),
]);
for r in &b.rows {
v.extend([r.desc.as_str(), r.date_acq.as_str(), r.how.as_str()]);
v.extend(r.fmv.fields());
v.extend(r.cost.fields());
v.extend(r.deduction.fields());
}
v
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct ScheduleDMap {
pub form: String,
pub year: i32,
pub line3: AmountCols,
pub line7_h: String,
pub line10: AmountCols,
pub line15_h: String,
pub line16_h: String,
#[serde(default = "default_sched_d_token")]
pub table_token: String,
#[serde(default)]
pub qof_yes: Option<CheckChoice>,
#[serde(default)]
pub qof_no: Option<CheckChoice>,
}
fn default_sched_d_token() -> String {
"Table_PartI".to_string()
}
impl ScheduleDMap {
pub fn parse(toml_src: &str) -> Result<Self, toml::de::Error> {
toml::from_str(toml_src)
}
pub fn ty2025() -> Self {
Self::parse(SCHEDULE_D_MAP_2025).expect("bundled schedule_d 2025 map parses")
}
pub fn ty2024() -> Self {
Self::parse(SCHEDULE_D_MAP_2024).expect("bundled schedule_d 2024 map parses")
}
pub fn ty2017() -> Self {
Self::parse(SCHEDULE_D_MAP_2017).expect("bundled schedule_d 2017 map parses")
}
pub fn for_year(year: i32) -> Result<Self, FormsError> {
match year {
2017 => Ok(Self::ty2017()),
2024 => Ok(Self::ty2024()),
2025 => Ok(Self::ty2025()),
_ => Err(FormsError::UnsupportedYear(year)),
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct ScheduleSeMap {
pub form: String,
pub year: i32,
pub line2: MoneyCell,
pub line3: MoneyCell,
pub line4a: MoneyCell,
pub line4c: MoneyCell,
pub line6: MoneyCell,
pub line8a: MoneyCell,
pub line8d: MoneyCell,
pub line9: MoneyCell,
pub line10: MoneyCell,
pub line11: MoneyCell,
pub line12: MoneyCell,
pub line13: MoneyCell,
#[serde(default)]
pub prefilled_exempt: Vec<String>,
}
impl ScheduleSeMap {
pub fn parse(toml_src: &str) -> Result<Self, toml::de::Error> {
toml::from_str(toml_src)
}
pub fn ty2025() -> Self {
Self::parse(SCHEDULE_SE_MAP_2025).expect("bundled schedule_se 2025 map parses")
}
pub fn ty2024() -> Self {
Self::parse(SCHEDULE_SE_MAP_2024).expect("bundled schedule_se 2024 map parses")
}
pub fn ty2017() -> Self {
Self::parse(SCHEDULE_SE_MAP_2017).expect("bundled schedule_se 2017 map parses")
}
pub fn for_year(year: i32) -> Result<Self, FormsError> {
match year {
2017 => Ok(Self::ty2017()),
2024 => Ok(Self::ty2024()),
2025 => Ok(Self::ty2025()),
_ => Err(FormsError::UnsupportedYear(year)),
}
}
pub fn lines(&self) -> [&MoneyCell; 12] {
[
&self.line2,
&self.line3,
&self.line4a,
&self.line4c,
&self.line6,
&self.line8a,
&self.line8d,
&self.line9,
&self.line10,
&self.line11,
&self.line12,
&self.line13,
]
}
pub fn field_names(&self) -> Vec<&str> {
self.lines().iter().flat_map(|c| c.fields()).collect()
}
}