// SPDX-FileCopyrightText: 2023 Aravinth Manivannan // // SPDX-License-Identifier: AGPL-3.0-or-later use std::rc::Rc; use rust_decimal::prelude::*; use rust_decimal_macros::dec; use crate::actor::*; use crate::purchase::*; use crate::strategy::*; #[derive(Clone)] pub struct Split { pub(crate) purchase: Rc, pub(crate) split_strategy: SplitStrategy, pub(crate) parties: Vec>, } impl Split { pub fn new( purchase: Rc, split_strategy: SplitStrategy, parties: Vec>, ) -> Self { Self { split_strategy, parties, purchase, } } pub fn money_owed(&self) -> ShareHashMap { match &self.split_strategy { SplitStrategy::Unequal(shares) => { let mut s = shares.clone(); let payer = self.purchase.paid_by(); if let Some((_, money)) = s.get_mut(payer.name()) { *money = dec!(0.0); } s } SplitStrategy::Equal => { let mut shares = ShareHashMap::default(); let cost = self.purchase.cost(); let total_parties = Decimal::new(self.parties.len() as i64, 0); for actor in self.parties.iter() { if *actor == self.purchase.paid_by() { shares.insert(actor.name().to_owned(), (actor.clone(), dec!(0.00))); } else { shares.insert( actor.name().to_owned(), (actor.clone(), cost / total_parties), ); } } shares } SplitStrategy::Shares(c) => { let mut shares = ShareHashMap::default(); let cost = self.purchase.cost(); for (actor_name, (actor, share)) in c.iter() { //if actor.name() == split.purchase.paid_by().name() { if *actor == self.purchase.paid_by() { shares.insert(actor_name.clone(), (actor.clone(), dec!(0.00))); } else { shares.insert(actor_name.clone(), (actor.clone(), cost * share)); } } shares } } } pub fn get(&mut self, actor: Rc) -> Option { unimplemented!(); // if let Some((_actor, cost)) = self.shares.get(actor.name()) { // Some(*cost) // } else { // None // } } pub fn validate(&self) -> bool { self.split_strategy.validate(self.purchase.as_ref()) } } //impl Actor for Person { // fn spends(&self, i: &I); // fn owes(&self, amount: usize); //} #[cfg(test)] mod tests { use super::*; use uuid::Uuid; #[test] fn split_works() { let person1: Rc = Rc::new(Person::new("person1".into(), Uuid::new_v4())); let person2: Rc = Rc::new(Person::new("person2".into(), Uuid::new_v4())); let person3: Rc = Rc::new(Person::new("person3".into(), Uuid::new_v4())); let person4: Rc = Rc::new(Person::new("person4".into(), Uuid::new_v4())); let item = Item::new("test item".into(), dec!(240.00), person1.clone()); let actors = vec![ person1.clone(), person2.clone(), person3.clone(), person4.clone(), ]; // equal split let strategy = SplitStrategy::Equal; let mut split = Split::new(Rc::new(item), strategy, actors); assert!(split.validate()); let dues = split.money_owed(); for (_actor_name, (actor, due)) in dues.iter() { if split.purchase.paid_by().name() == actor.name() { assert_eq!(*due, dec!(0.00)); } else { assert_eq!( *due, split.purchase.cost() / Decimal::new(split.parties.len() as i64, 0) ); } } // share split let mut shares = ShareHashMap::default(); shares.insert(person1.name().to_owned(), (person1.clone(), dec!(0.3))); shares.insert(person2.name().to_owned(), (person2.clone(), dec!(0.4))); shares.insert(person3.name().to_owned(), (person3.clone(), dec!(0.2))); shares.insert(person4.name().to_owned(), (person4.clone(), dec!(0.1))); let strategy = SplitStrategy::Shares(shares.clone()); split.split_strategy = strategy; assert!(split.validate()); let dues = split.money_owed(); for (actor_name, (actor, due)) in dues.iter() { println!("Testing for actor: {actor_name} with due: {due}"); if split.purchase.paid_by().name() == actor.name() { assert_eq!(*due, dec!(0.00)); } else { let (_, share) = shares.get(actor_name).unwrap(); assert_eq!(*due, split.purchase.cost() * *share); } } // Unequal split let mut shares = ShareHashMap::default(); shares.insert( person1.name().to_owned(), (person1.clone(), dec!(0.3) * split.purchase.cost()), ); shares.insert( person2.name().to_owned(), (person2.clone(), dec!(0.4) * split.purchase.cost()), ); shares.insert( person3.name().to_owned(), (person3.clone(), dec!(0.2) * split.purchase.cost()), ); shares.insert( person4.name().to_owned(), (person4.clone(), dec!(0.1) * split.purchase.cost()), ); let strategy = SplitStrategy::Unequal(shares); split.split_strategy = strategy; assert!(split.validate()); } }