Organized Calculator

This commit is contained in:
Maddox Werts 2025-08-02 15:26:55 -04:00
parent 1627998e1e
commit 241ca41d83
2 changed files with 12 additions and 148 deletions

View file

@ -86,7 +86,7 @@ impl App {
// Making the Backend Calculate This
let calculation = backend
.calculate(
.calculator_start(
current_membership.into(),
new_membership.into(),
last_billing.into(),

View file

@ -1,55 +1,13 @@
// Libraries
use chrono::{self, Datelike, Local, NaiveDate};
use regex::Regex;
use std::io::{Error, ErrorKind, Result};
mod calculator;
use std::io::Result;
// Functions
fn extract_price(input: String) -> Option<f32> {
let re = Regex::new(r"\$?(\d+\.\d+)").unwrap();
re.captures(&input).and_then(|caps| {
caps.get(1).and_then(|m| {
let num_str = m.as_str().replace(',', "");
num_str.parse::<f32>().ok()
})
})
}
fn add_months(date: &NaiveDate, months: i32) -> NaiveDate {
// Getting all required dates for our addition
let mut year = date.year();
let mut month = date.month() as i32 + months;
let day = date.day();
// Adjust year and month to correct values
if month > 12 {
year += (month - 1) / 12;
month = (month - 1) % 12 + 1;
} else if month < 1 {
year -= (12 - month) / 12 + 1;
month = 12 - ((12 - month) % 12);
}
// Trying to build date on target year/month/day - if invalid (e.g. 31st Feb), fallback:
// We first try creating date with the original day
if let Some(new_date) = NaiveDate::from_ymd_opt(year, month as u32, day) {
new_date
} else {
// If invalid day, set day to last day of previous month by setting day = 0 on next month
// Since NaiveDate::from_ymd_opt(year, month + 1, 0) is last day of month
NaiveDate::from_ymd_opt(year, month as u32 + 1, 0).expect("Invalid date calculation")
}
}
use calculator::{CalculationResult, Calculator};
// Structures
pub struct Backend {
memberships: Vec<String>,
}
pub struct CalculationResult {
pub change_reg: f32,
pub change_abs: f32,
pub date_str: String,
pub date_num: i32,
pub reversed: bool,
pub invalid: bool,
calculator: Calculator,
}
// Implementations
@ -80,7 +38,10 @@ impl Backend {
};
// Return Self
Ok(Self { memberships })
Ok(Self {
memberships,
calculator: Calculator::new(),
})
}
// Functions
@ -93,110 +54,13 @@ impl Backend {
pub fn get_memberships(&self) -> Vec<String> {
self.memberships.clone()
}
/// Calculates the Prorate with the given parameters
///
/// # Arguments
///
/// # Examples
pub fn calculate(
pub fn calculator_start(
&self,
current_membership: String,
new_membership: String,
last_billing: String,
) -> Result<CalculationResult> {
// Extracting the Prices from our Memberships
let current_membership = match extract_price(current_membership) {
Some(m) => m,
None => {
return Err(Error::new(
ErrorKind::InvalidData,
"No Price Found (Current).",
))
}
};
let new_membership = match extract_price(new_membership) {
Some(m) => m,
None => return Err(Error::new(ErrorKind::InvalidData, "No Price Found (New).")),
};
// Getting the Dates
let last_billing = match NaiveDate::parse_from_str(&last_billing, "%m/%d/%Y") {
Ok(date) => date,
Err(_) => return Err(Error::new(ErrorKind::InvalidData, "Date Format Incorrect.")),
};
let current_date = Local::now().date_naive();
// Getting the amount of Months that have passed
let mut months_passed = (current_date.year() - last_billing.year()) * 12
+ (current_date.month() as i32 - last_billing.month() as i32);
// Saftey Check
if current_date.day() < last_billing.day() {
months_passed -= 1;
}
// Are the amount of months that have passed less than 0?
if months_passed < 0 {
months_passed = 0;
}
// Getting the Cost Difference
let cost_difference = new_membership - current_membership;
// Stop if there's no price difference
if cost_difference == 0.0 {
return Ok(CalculationResult {
change_reg: 0.0,
change_abs: 0.0,
date_str: String::new(),
date_num: 0,
reversed: false,
invalid: true,
});
}
// Getting the billing Start & End
let billing_start = add_months(&last_billing, months_passed);
let billing_end = add_months(&last_billing, months_passed + 1);
// Getting the days left in our billing cycle
let days_in_cycle = (billing_end - billing_start).num_days() as f32;
// Getting our daily rate in our plan
let current_daily_rate = current_membership / days_in_cycle;
// Reversing the adjustment logic here
let adjustment_days = (-cost_difference / current_daily_rate * 10.0).round() / 10.0;
// Getting the Billing Dates
let billing_date_normal = add_months(&last_billing, months_passed + 1);
let adjustment_days_int = adjustment_days.round() as i64;
let billing_date_adjusted = if adjustment_days_int >= 0 {
billing_date_normal.checked_add_signed(chrono::Duration::days(adjustment_days_int))
} else {
billing_date_normal.checked_add_signed(chrono::Duration::days(-adjustment_days_int))
}
.unwrap();
// Getting the Adjustment Days
let adjustment_days_f32 = adjustment_days.abs();
// Getting the Adjustment Days String
let adjusted_date_string = billing_date_adjusted.format("%m/%d/%Y").to_string();
let date_number = billing_date_adjusted
.format("%d")
.to_string()
.parse::<i32>()
.unwrap_or(0);
// Ok!!
Ok(CalculationResult {
change_abs: adjustment_days_f32,
change_reg: adjustment_days,
date_str: adjusted_date_string,
date_num: date_number,
reversed: adjustment_days <= 0.0,
invalid: false,
})
self.calculator
.start(current_membership, new_membership, last_billing)
}
}