use super::funejson::Value; use super::math_round_div::round_div; /// The weight a carrier charges on: the greater of actual and volumetric, /// rounded up to the carrier's billing step. /// /// mm³ / (cm³ per kg) is exactly grams, so everything stays in integers. /// /// # Panics /// Panics on a dimension below 1 mm, a negative weight, or a divisor or /// billing step below 1. pub fn chargeable_weight( length_mm: i64, width_mm: i64, height_mm: i64, actual_grams: i64, divisor: i64, round_up_to_grams: i64, ) -> ChargeableWeight { for d in [length_mm, width_mm, height_mm] { if d < 1 { panic!("dimensions must be 1 mm or more, received {}", d); } } if actual_grams < 0 { panic!("actualGrams must not be negative, received {}", actual_grams); } if divisor < 1 { panic!("divisor must be 1 or more, received {}", divisor); } if round_up_to_grams < 1 { panic!("roundUpToGrams must be 1 or more, received {}", round_up_to_grams); } let volumetric_grams = round_div(length_mm * width_mm * height_mm, divisor, "up"); let heavier = actual_grams.max(volumetric_grams); ChargeableWeight { actual_grams, volumetric_grams, chargeable_grams: round_div(heavier, round_up_to_grams, "up") * round_up_to_grams, basis: if volumetric_grams > actual_grams { "volumetric".to_string() } else { "actual".to_string() }, } } pub fn chargeable_weight_to_value(w: &ChargeableWeight) -> Value { Value::obj(vec![ ("actualGrams", Value::Int(w.actual_grams)), ("volumetricGrams", Value::Int(w.volumetric_grams)), ("chargeableGrams", Value::Int(w.chargeable_grams)), ("basis", Value::str(&w.basis)), ]) } pub fn fune_vector(args: &[Value]) -> Value { chargeable_weight_to_value(&chargeable_weight( args[0].as_i64(), args[1].as_i64(), args[2].as_i64(), args[3].as_i64(), args[4].as_i64(), args[5].as_i64(), )) }