nyandere 0.1.2

i help with keeping track of purchases. meow
Documentation
use std::collections::btree_map::Entry;

use super::prelude::*;

/// Move money from *source* to *target*.
///
/// This modifies the [`super::Balance`] ***negatively***!
/// The balance denotes how much *source* owes the *target*,
/// which is how much *source* would need to pay *target*
/// to be on 0 again.
///
/// For example, `A` paying 1€ to `B` means
/// the balance from `A` to `B` will be -1€.
#[apply(cmd_args!)]
pub struct Pay {
    #[pos]
    pub amount: Money,
    #[named]
    pub who: Dir,
}

impl Command for Pay {
    type Args = Self;
}

impl Run for Pay {
    fn run(self: Box<Self>, ctx: &mut State) -> Output {
        let mut difference: Value = self.amount.into();
        difference.align_to(&self.who);

        match ctx.balances.entry(self.who.into()) {
            Entry::Occupied(mut entry) => *entry.get_mut() += difference,
            Entry::Vacant(entry) => {
                entry.insert(difference);
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use crate::{Set, eval, ext::math::Integer};

    #[test]
    fn basic() {
        let rt = eval(
            "
            create entity A
            create entity B
            create entity C

            pay 1€ from=A to=B
            pay 5€ from=B to=A
            pay 3€ from=A to=C
            ",
        )
        .unwrap();

        let s = |lit: &str| lit.to_string();
        let i = Integer::from;

        assert_eq!(
            rt.to_state()
                .balances
                .into_iter()
                .map(|(pair, bal)| {
                    (
                        pair.into_iter()
                            .map(|e| e.name().to_owned())
                            .collect::<Vec<_>>(),
                        bal.0,
                    )
                })
                .collect::<Set<_>>(),
            [
                (vec![s("A"), s("B")], i(-400)),
                (vec![s("A"), s("C")], i(300))
            ]
            .into_iter()
            .collect::<Set<_>>(),
        );
    }
}