generated from OBJNULL/Dockerized-Rust
Compare commits
2 commits
1627998e1e
...
31601a2dd6
Author | SHA1 | Date | |
---|---|---|---|
31601a2dd6 | |||
241ca41d83 |
3 changed files with 185 additions and 148 deletions
|
@ -86,7 +86,7 @@ impl App {
|
||||||
|
|
||||||
// Making the Backend Calculate This
|
// Making the Backend Calculate This
|
||||||
let calculation = backend
|
let calculation = backend
|
||||||
.calculate(
|
.calculator_start(
|
||||||
current_membership.into(),
|
current_membership.into(),
|
||||||
new_membership.into(),
|
new_membership.into(),
|
||||||
last_billing.into(),
|
last_billing.into(),
|
||||||
|
|
|
@ -1,55 +1,13 @@
|
||||||
// Libraries
|
// Libraries
|
||||||
use chrono::{self, Datelike, Local, NaiveDate};
|
mod calculator;
|
||||||
use regex::Regex;
|
use std::io::Result;
|
||||||
use std::io::{Error, ErrorKind, Result};
|
|
||||||
|
|
||||||
// Functions
|
use calculator::{CalculationResult, Calculator};
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Structures
|
// Structures
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
memberships: Vec<String>,
|
memberships: Vec<String>,
|
||||||
}
|
calculator: Calculator,
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementations
|
// Implementations
|
||||||
|
@ -80,7 +38,10 @@ impl Backend {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return Self
|
// Return Self
|
||||||
Ok(Self { memberships })
|
Ok(Self {
|
||||||
|
memberships,
|
||||||
|
calculator: Calculator::new(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
@ -93,110 +54,13 @@ impl Backend {
|
||||||
pub fn get_memberships(&self) -> Vec<String> {
|
pub fn get_memberships(&self) -> Vec<String> {
|
||||||
self.memberships.clone()
|
self.memberships.clone()
|
||||||
}
|
}
|
||||||
/// Calculates the Prorate with the given parameters
|
pub fn calculator_start(
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
pub fn calculate(
|
|
||||||
&self,
|
&self,
|
||||||
current_membership: String,
|
current_membership: String,
|
||||||
new_membership: String,
|
new_membership: String,
|
||||||
last_billing: String,
|
last_billing: String,
|
||||||
) -> Result<CalculationResult> {
|
) -> Result<CalculationResult> {
|
||||||
// Extracting the Prices from our Memberships
|
self.calculator
|
||||||
let current_membership = match extract_price(current_membership) {
|
.start(current_membership, new_membership, last_billing)
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
173
project/src/backend/calculator.rs
Normal file
173
project/src/backend/calculator.rs
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
// Libraries
|
||||||
|
use chrono::{self, Datelike, Local, NaiveDate};
|
||||||
|
use regex::Regex;
|
||||||
|
use std::io::{Error, ErrorKind, Result};
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
pub 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structures
|
||||||
|
pub struct Calculator {}
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementations
|
||||||
|
impl Calculator {
|
||||||
|
// Constructors
|
||||||
|
/// Creates a new Calculator
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rs
|
||||||
|
/// let calculator = Calculator::new();
|
||||||
|
/// ```
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
/// Calculates the Prorate with the given parameters
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
pub fn 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue