From 72f4dfceb17c14013e89ae607e31ff17ec598cef Mon Sep 17 00:00:00 2001 From: Maddox Werts Date: Fri, 1 Aug 2025 15:51:39 -0400 Subject: [PATCH] Created the `calculate()` function --- project/src/backend.rs | 143 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/project/src/backend.rs b/project/src/backend.rs index fd729a1..206021f 100644 --- a/project/src/backend.rs +++ b/project/src/backend.rs @@ -1,10 +1,54 @@ // Libraries -use std::io::Result; +use chrono::{self, Datelike, Local, NaiveDate}; +use regex::Regex; +use std::io::{Error, ErrorKind, Result}; + +// Functions +fn extract_price(input: String) -> Option { + 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::().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") + } +} // Structures pub struct Backend { memberships: Vec, } +pub struct CalculationResult { + pub days_pushed: f32, + pub new_date: String, + pub reversed: bool, + pub invalid: bool, +} // Implementations impl Backend { @@ -47,4 +91,101 @@ impl Backend { pub fn get_memberships(&self) -> Vec { self.memberships.clone() } + /// Calculates the Prorate with the given parameters + /// + /// # Arguments + /// + /// # Examples + pub fn calculate( + &self, + current_membership: String, + new_membership: String, + last_billing: String, + ) -> Result { + // 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 { + days_pushed: 0.0, + new_date: String::new(), + 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(); + + // Ok!! + Ok(CalculationResult { + days_pushed: adjustment_days_f32, + new_date: adjusted_date_string, + reversed: adjustment_days <= 0.0, + invalid: false, + }) + } }