Skip to content

Instantly share code, notes, and snippets.

@archer884
Created August 28, 2019 16:54
Show Gist options
  • Save archer884/51bac2d35323dccbbc9cc2c69a304c20 to your computer and use it in GitHub Desktop.
Save archer884/51bac2d35323dccbbc9cc2c69a304c20 to your computer and use it in GitHub Desktop.
Taxes
// 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 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 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