Created
August 28, 2019 16:54
-
-
Save archer884/51bac2d35323dccbbc9cc2c69a304c20 to your computer and use it in GitHub Desktop.
Taxes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file performs the stegasaurus recovery routine in order to fetch the tax brackets from | |
// your provided photograph. | |
use serde::Deserialize; | |
use std::fs; | |
use std::io::Cursor; | |
use std::path::Path; | |
#[derive(Debug, Deserialize)] | |
pub struct Bracket { | |
pub limit: Option<i32>, | |
pub rate: i32, | |
} | |
pub fn default_brackets() -> &'static [Bracket] { | |
static BRACKETS: &[Bracket] = &[ | |
Bracket { | |
limit: Some(10_000), | |
rate: 0, | |
}, | |
Bracket { | |
limit: Some(30_000), | |
rate: 10, | |
}, | |
Bracket { | |
limit: Some(100_000), | |
rate: 25, | |
}, | |
Bracket { | |
limit: None, | |
rate: 40, | |
}, | |
]; | |
BRACKETS | |
} | |
pub fn from_path(path: impl AsRef<Path>) -> crate::Result<Vec<Bracket>> { | |
let cursor = Cursor::new(extract(path)?); | |
Ok(serde_json::from_reader(cursor)?) | |
} | |
fn extract(path: impl AsRef<Path>) -> crate::Result<Vec<u8>> { | |
let carrier = fs::read(path)?; | |
let mut buffer = Vec::new(); | |
stegasaurus::recover(&carrier, &mut buffer)?; | |
Ok(buffer) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This program makes use of a library called stegasaurus (sic) to support steganographic storage | |
// of tax brackets. In essence, it's possible to store your modified tax brackets in a family | |
// photograph and send that photo to the tax office to get your tax amount. That way, they can't | |
// directly observe what your brackets are. | |
mod bracket; | |
mod tax; | |
use structopt::StructOpt; | |
// In theory, we want to avoid floats to store monetary amounts, and we want to avoid possible | |
// rounding errors resulting from using integers. Hence the fixed-precision number. Whether this | |
// is better than a rational representation or not, I have no idea. | |
type Decimal = fixed::FixedI64<fixed::frac::U32>; | |
// I just don't want to write this more than once. Of course, that's true of the last type, too... | |
// Note that it's not so much that these names can't be shortened (by adding more uses like on | |
// line four above) but just that I don't want to add a line for something I'm only going to have | |
// in one place ever. | |
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>; | |
#[derive(Debug, StructOpt)] | |
struct Opt { | |
income: i32, | |
brackets: Option<String>, | |
} | |
// Possible failure modes: | |
// 1. Unable to read from brackets file. | |
// 2. Unable to parse tax brackets from file. | |
fn main() -> Result<()> { | |
let opt = Opt::from_args(); | |
let tax = match opt.brackets { | |
None => tax::calculate(opt.income, bracket::default_brackets()), | |
Some(path) => tax::calculate(opt.income, bracket::from_path(path)?), | |
}; | |
println!("{}", tax); | |
Ok(()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file presents the logic used to perform the tax calculations themselves. If you look down | |
// at the bottom, you can also see an example of inline unit testing. | |
use crate::{bracket::Bracket, Decimal}; | |
use std::borrow::Borrow; | |
use std::fmt::{self, Display}; | |
pub fn calculate<I, B>(n: i32, brackets: I) -> Tax | |
where | |
I: IntoIterator<Item = B>, | |
B: Borrow<Bracket>, | |
{ | |
let mut tax = Tax::new(n); | |
// Not gonna lie: I just had an epiphany and realized what the Borrow trait is for. | |
brackets | |
.into_iter() | |
.for_each(|bracket| tax.apply(bracket.borrow())); | |
tax | |
} | |
pub struct Tax { | |
amt: i32, | |
app: i32, | |
tax: Decimal, | |
} | |
impl Tax { | |
pub fn new(n: i32) -> Self { | |
Self { | |
amt: n, | |
app: 0, | |
tax: Decimal::from(0), | |
} | |
} | |
pub fn apply(&mut self, bracket: &Bracket) { | |
// All the money has been taxed. | |
if self.app == self.amt { | |
return; | |
} | |
match bracket.limit { | |
None => { | |
let n = Decimal::from(self.amt - self.app); | |
self.tax += n * Decimal::from(bracket.rate) / Decimal::from(100); | |
self.app += n.saturating_to_num::<i32>(); | |
} | |
Some(limit) => { | |
let n = Decimal::from(limit.min(self.amt) - self.app); | |
self.tax += n * Decimal::from(bracket.rate) / Decimal::from(100); | |
self.app += n.saturating_to_num::<i32>(); | |
} | |
} | |
} | |
fn bill(&self) -> i32 { | |
self.tax.saturating_to_num() | |
} | |
} | |
impl Display for Tax { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
let bill = self.bill(); | |
let rate = f64::from(bill) / f64::from(self.amt) * 100.0; | |
write!(f, "{} ({:.1}%)", bill, rate) | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use crate::{bracket::default_brackets, tax}; | |
// Not the world's best name for a test. | |
#[test] | |
fn it_works() { | |
assert_eq!(tax::calculate(0, default_brackets()).bill(), 0); | |
assert_eq!(tax::calculate(10000, default_brackets()).bill(), 0); | |
assert_eq!(tax::calculate(10009, default_brackets()).bill(), 0); | |
assert_eq!(tax::calculate(10010, default_brackets()).bill(), 1); | |
assert_eq!(tax::calculate(12000, default_brackets()).bill(), 200); | |
assert_eq!(tax::calculate(56789, default_brackets()).bill(), 8697); | |
assert_eq!(tax::calculate(1234567, default_brackets()).bill(), 473326); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment