vz-tools-cli 0.1.2

A CLI for VZ
Documentation
use chrono::Utc;
use clap::{value_parser, Arg, Command};
use colored::Colorize;

use vz_tools_cli::db;

fn main() {
    let matches = Command::new("vz-tools/cli")
        .version("0.1.0")
        .author("notgiorgi <notgiorgi@gmail.com>")
        .about("Virtual zone dividend tracker for easy tax filling")
        .subcommand(
            Command::new("dividend")
                .arg(
                    Arg::new("amount") //
                        .help("Net money amount")
                        .value_parser(value_parser!(f64))
                        .required(true),
                )
                .arg(
                    Arg::new("dry")
                        .long("dry")
                        .short('d')
                        .value_parser(value_parser!(bool))
                        .default_value("false")
                        .default_missing_value("true")
                        .num_args(0..=1)
                        .help("if true tx wont be persisted"),
                )
                .about("Calculates dividend from net amount and optionally persists it locally"),
        )
        .subcommand(
            Command::new("get-latest")
                .arg(
                    Arg::new("instructions")
                        .short('i')
                        .long("instructions")
                        .default_value("false")
                        .default_missing_value("true")
                        .num_args(0..=1)
                        .value_parser(value_parser!(bool))
                        .help("Prints declaration filling instructions if set to true"),
                )
                .about("Retrieves latest persisted transaction"),
        )
        .subcommand(Command::new("get-all").about("Prints out all the past persisted transactions"))
        .get_matches();

    // println!("{:#?}", matches.subcommand());
    match matches.subcommand() {
        Some(("dividend", args)) => {
            let amount = *args.get_one::<f64>("amount").expect("amount is required");
            let dry = *args
                .get_one::<bool>("dry")
                .expect("dry has a default value");

            let (amount, reminder) = round_down(amount);
            let (dividend, tax) = calculate_dividend(amount);

            if dry {
                println!("{}", "Dry run, transaction won't be persisted".bold());
                print!("{}", format_dividend_instructions(dividend, tax, reminder));
                return;
            }

            let date = Utc::now().to_rfc3339();

            let conn = db::db_connect().expect("Couldn't connect to db");

            let tx = db::Tx::new(date, amount, dividend, tax, reminder);

            db::insert_tx(&conn, &tx).expect("Couldn't insert tx, try again");

            println!("{}", "Transaction persisted".bold());
            println!();
            print_header();
            println!("{}", tx);
        }
        Some(("get-latest", args)) => {
            let instructions = *args
                .get_one::<bool>("instructions")
                .expect("instructions has a default value");

            let conn = db::db_connect().expect("Couldn't connect to db");

            let tx = db::find_latest_tx(&conn).expect("Couldn't find latest tx");

            println!("{}", "Latest tx".bold());
            println!();
            print_header();
            println!("{}", tx);
            if instructions {
                print_instructions();
            }
        }
        Some(("get-all", _args)) => {
            let conn = db::db_connect().expect("Couldn't connect to db");

            let txs = db::find_all_tx(&conn).expect("Couldn't find txs");

            println!("{}", "Last 20 txs".bold());
            println!();
            print_header();
            for tx in txs {
                println!("{}", tx);
            }
        }
        _ => println!("no commands"),
    }
}

fn round_down(net: f64) -> (f64, f64) {
    let net_int = net as i32;
    let new_net = net_int - net_int % 20;
    let mut remainder = 0.0;
    if new_net != net_int {
        remainder = net - (new_net as f64);
    }
    return (new_net as f64, remainder);
}

fn calculate_dividend(net: f64) -> (f64, f64) {
    return (net * 0.95, net * 0.05);
}

fn format_dividend_instructions(dividend: f64, tax: f64, remainder: f64) -> String {
    return format!(
        r#"
{}: {}
{}: {}
{}: {}
"#,
        "Dividend".bold(),
        dividend.to_string().underline(),
        "Tax".bold(),
        tax.to_string().underline(),
        "Remainder".bold(),
        remainder.to_string().underline()
    );
}

fn print_header() {
    println!(
        "| {0: <15} | {1: <15} | {2: <15} | {3: <15} | {4: <15}",
        "Date".bold(),
        "Net amount".bold(),
        "Dividend".bold(),
        "Tax".bold(),
        "Remainder".bold()
    );
}

fn print_instructions() {
    println!(
        "\
დეკლარაციის ტიპი: საშემოსავლო (გადახდის წყაროსთან დაკავებული გადასახადი)
საქმიანობის კოდი: 62020
დანარჩენი: https://virtualzonetools.github.io/virtual-person-taxes/
"
    );
}