#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilingStatus {
Single,
Joint,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StandardDeduction {
pub base: f64,
pub temporary_addon: f64,
}
impl StandardDeduction {
pub const SINGLE_2025: StandardDeduction = StandardDeduction {
base: 15_750.0,
temporary_addon: 750.0,
};
pub const JOINT_2025: StandardDeduction = StandardDeduction {
base: 31_500.0,
temporary_addon: 1_500.0,
};
#[inline]
pub fn total(&self) -> f64 {
self.base + self.temporary_addon
}
}
pub fn addon_applies(year: u32) -> bool {
(2025..=2028).contains(&year)
}
pub fn standard_deduction(status: FilingStatus, year: u32) -> f64 {
standard_deduction_with(status, year, |s, _| s.default_2025())
}
pub fn standard_deduction_with<F>(status: FilingStatus, year: u32, lookup: F) -> f64
where
F: Fn(FilingStatus, u32) -> StandardDeduction,
{
let sd = lookup(status, year);
if addon_applies(year) {
sd.total()
} else {
sd.base
}
}
impl FilingStatus {
pub fn default_2025(self) -> StandardDeduction {
match self {
FilingStatus::Single => StandardDeduction::SINGLE_2025,
FilingStatus::Joint => StandardDeduction::JOINT_2025,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct QualifiedDeductionParams {
pub cap: f64,
pub phase_out_start: f64,
pub phase_out_rate: f64,
}
impl QualifiedDeductionParams {
pub const SINGLE: QualifiedDeductionParams = QualifiedDeductionParams {
cap: 12_500.0,
phase_out_start: 150_000.0,
phase_out_rate: 0.1,
};
pub const JOINT: QualifiedDeductionParams = QualifiedDeductionParams {
cap: 25_000.0,
phase_out_start: 300_000.0,
phase_out_rate: 0.1,
};
#[inline]
pub fn phase_out_end(&self) -> f64 {
self.phase_out_start + self.cap / self.phase_out_rate
}
}
impl FilingStatus {
pub fn default_qualified_params(self) -> QualifiedDeductionParams {
match self {
FilingStatus::Single => QualifiedDeductionParams::SINGLE,
FilingStatus::Joint => QualifiedDeductionParams::JOINT,
}
}
}
pub fn qualified_effective_cap(magi: f64, p: QualifiedDeductionParams) -> f64 {
if magi <= p.phase_out_start {
p.cap
} else if magi >= p.phase_out_end() {
0.0
} else {
let over = magi - p.phase_out_start;
(p.cap - over * p.phase_out_rate).max(0.0)
}
}
pub fn overtime_deduction(qualified_overtime: f64, magi: f64, status: FilingStatus) -> f64 {
qualified_deduction(qualified_overtime, magi, status)
}
pub fn tips_deduction(qualified_tips: f64, magi: f64, status: FilingStatus) -> f64 {
qualified_deduction(qualified_tips, magi, status)
}
fn qualified_deduction(amount: f64, magi: f64, status: FilingStatus) -> f64 {
let amt = amount.max(0.0);
let cap = qualified_effective_cap(magi, status.default_qualified_params());
amt.min(cap)
}
pub fn total_deduction(
status: FilingStatus,
year: u32,
qualified_overtime: f64,
qualified_tips: f64,
magi: f64,
) -> f64 {
let sd = standard_deduction(status, year);
let ot = overtime_deduction(qualified_overtime, magi, status);
let tips = tips_deduction(qualified_tips, magi, status);
sd + ot + tips
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn standard_deduction_single_2025_includes_addon() {
assert!((standard_deduction(FilingStatus::Single, 2025) - 16_500.0).abs() < 1e-6);
}
#[test]
fn standard_deduction_joint_2025_includes_addon() {
assert!((standard_deduction(FilingStatus::Joint, 2025) - 33_000.0).abs() < 1e-6);
}
#[test]
fn addon_sunsets_after_2028() {
assert!((standard_deduction(FilingStatus::Single, 2029) - 15_750.0).abs() < 1e-6);
assert!((standard_deduction(FilingStatus::Joint, 2029) - 31_500.0).abs() < 1e-6);
}
#[test]
fn addon_window_boundaries() {
assert!(addon_applies(2025));
assert!(addon_applies(2028));
assert!(!addon_applies(2024));
assert!(!addon_applies(2029));
}
#[test]
fn overtime_deduction_under_threshold_full() {
let d = overtime_deduction(8_000.0, 90_000.0, FilingStatus::Single);
assert!((d - 8_000.0).abs() < 1e-6);
}
#[test]
fn overtime_deduction_capped() {
let d = overtime_deduction(40_000.0, 90_000.0, FilingStatus::Single);
assert!((d - 12_500.0).abs() < 1e-6);
}
#[test]
fn tips_deduction_capped_joint() {
let d = tips_deduction(40_000.0, 200_000.0, FilingStatus::Joint);
assert!((d - 25_000.0).abs() < 1e-6);
}
#[test]
fn qualified_phase_out_linear() {
let p = QualifiedDeductionParams::SINGLE;
assert!((qualified_effective_cap(160_000.0, p) - 11_500.0).abs() < 1e-6);
assert!((p.phase_out_end() - 275_000.0).abs() < 1e-6);
assert_eq!(qualified_effective_cap(300_000.0, p), 0.0);
}
#[test]
fn total_deduction_stacks_all_three() {
let t = total_deduction(FilingStatus::Single, 2025, 8_000.0, 5_000.0, 90_000.0);
assert!((t - 29_500.0).abs() < 1e-6);
}
#[test]
fn total_deduction_respects_caps() {
let t = total_deduction(FilingStatus::Single, 2025, 40_000.0, 40_000.0, 90_000.0);
assert!((t - 41_500.0).abs() < 1e-6);
}
#[test]
fn negative_amounts_clamped() {
assert_eq!(overtime_deduction(-1_000.0, 90_000.0, FilingStatus::Single), 0.0);
}
}