Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::money_allocate::allocate; ← from money.allocate ^1.0.0 · built alongside by fune
use super::money_amount::{money, money_from_value, money_to_value, Money}; ← from money.amount ^1.0.0 · built alongside by fune
fn weight_of(member: &TroncMember, method: &str) -> i64 {
match method {
"hours" => member.minutes_worked,
"points" => member.points,
"hours-and-points" => member.minutes_worked * member.points,
other => panic!("unknown tronc method \"{}\"", other),
}
}
/// Share a tips pool between staff in proportion to a weight.
///
/// Time is in minutes so a 7.5-hour shift is exact. The pool is split with
/// money.allocate, so the shares add up to the pool exactly: the leftover
/// pennies go to the largest remainders, ties to whoever is listed first.
///
/// # Panics
/// Panics on an empty pool of staff, a negative pool, duplicate ids, negative
/// minutes or points, an unknown method, or a pool nobody has weight in.
pub fn tronc_allocation(pool: &Money, staff: &[TroncMember], method: &str) -> Vec<TroncShare> {
if staff.is_empty() {
panic!("staff must have at least one member");
}
if pool.minor < 0 {
panic!("pool must not be negative, received {}", pool.minor);
}
let mut ids: Vec<&str> = Vec::new();
for member in staff {
if member.id.is_empty() {
panic!("every member needs an id");
}
if ids.contains(&member.id.as_str()) {
panic!("staff id \"{}\" appears twice", member.id);
}
ids.push(&member.id);
if member.minutes_worked < 0 {
panic!(
"minutesWorked must not be negative, received {} for \"{}\"",
member.minutes_worked, member.id
);
}
if member.points < 0 {
panic!("points must not be negative, received {} for \"{}\"", member.points, member.id);
}
}
let weights: Vec<i64> = staff.iter().map(|m| weight_of(m, method)).collect();
if weights.iter().sum::<i64>() == 0 {
if pool.minor != 0 {
panic!("nobody in the pool has any weight to share it by");
}
return staff
.iter()
.map(|m| TroncShare { id: m.id.clone(), weight: 0, share: money(0, &pool.currency) })
.collect();
}
let shares = allocate(pool, &weights);
staff
.iter()
.zip(weights.iter().zip(shares))
.map(|(m, (w, s))| TroncShare { id: m.id.clone(), weight: *w, share: s })
.collect()
}
pub fn tronc_member_from_value(v: &Value) -> TroncMember {
TroncMember {
id: v.get("id").as_str().to_string(),
minutes_worked: v.get("minutesWorked").as_i64(),
points: v.get("points").as_i64(),
}
}
pub fn tronc_share_to_value(s: &TroncShare) -> Value {
Value::obj(vec![
("id", Value::str(&s.id)),
("weight", Value::Int(s.weight)),
("share", money_to_value(&s.share)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
let staff: Vec<TroncMember> = args[1].as_arr().iter().map(tronc_member_from_value).collect();
Value::Arr(
tronc_allocation(&money_from_value(&args[0]), &staff, args[2].as_str())
.iter()
.map(tronc_share_to_value)
.collect(),
)
}