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::inventory_pallet_fit_data::PALLETS; ← this capability’s own data, compiled from data/pallets.json into the same file by fune build
/// Cartons per layer on a `length` x `width` deck, base `a` x `b`, as the best
/// two-block pattern: one split across the length or the width, each block a
/// grid with the carton one way round.
fn cartons_per_layer(length: i64, width: i64, a: i64, b: i64) -> i64 {
let mut best = 0;
let mut i = 0;
while i * a <= length {
best = best.max(i * (width / b) + ((length - i * a) / b) * (width / a));
i += 1;
}
let mut j = 0;
while j * b <= width {
best = best.max(j * (length / a) + ((width - j * b) / a) * (length / b));
j += 1;
}
best
}
fn positive(name: &str, value: i64) {
if value <= 0 {
panic!("{} must be a whole number greater than zero, received {}", name, value);
}
}
/// The best stack of identical cartons on a UK or EUR pallet within the limits.
///
/// # Panics
/// Panics on a dimension that is not positive, a negative weight or weight
/// limit, or an unknown pallet.
pub fn pallet_fit(carton: &Carton, pallet: &str, max_load_height_mm: i64, max_load_weight_grams: Option<i64>, upright: bool) -> PalletFit {
positive("lengthMm", carton.length_mm);
positive("widthMm", carton.width_mm);
positive("heightMm", carton.height_mm);
if carton.weight_grams < 0 {
panic!("weightGrams must be a whole number, not negative, received {}", carton.weight_grams);
}
positive("maxLoadHeightMm", max_load_height_mm);
if let Some(mw) = max_load_weight_grams {
if mw < 0 {
panic!("maxLoadWeightGrams must be a whole number, not negative, received {}", mw);
}
}
let deck = match PALLETS.iter().find(|p| p.code == pallet) {
Some(d) => d,
None => panic!("unknown pallet \"{}\": expected uk or euro", pallet),
};
let (l, w, h) = (carton.length_mm, carton.width_mm, carton.height_mm);
let choices: Vec<(i64, i64, i64)> = if upright {
vec![(h, l, w)]
} else {
vec![(h, l, w), (w, l, h), (l, w, h)]
};
let mut best: Option<PalletFit> = None;
for (vertical, a, b) in choices {
let per_layer = cartons_per_layer(deck.length_mm, deck.width_mm, a, b);
let layers = max_load_height_mm / vertical;
let mut per_pallet = per_layer * layers;
let mut limited_by = if per_layer == 0 { "footprint" } else { "height" };
if let Some(mw) = max_load_weight_grams {
if per_layer > 0 && carton.weight_grams > 0 {
let by_weight = mw / carton.weight_grams;
if by_weight < per_pallet {
per_pallet = by_weight;
limited_by = "weight";
}
}
}
let fit = PalletFit {
pallet_length_mm: deck.length_mm,
pallet_width_mm: deck.width_mm,
carton_height_mm: vertical,
per_layer,
layers,
per_pallet,
limited_by: limited_by.to_string(),
load_height_mm: if per_layer == 0 { 0 } else { (per_pallet + per_layer - 1) / per_layer * vertical },
load_weight_grams: per_pallet * carton.weight_grams,
};
if best.as_ref().map_or(true, |b| fit.per_pallet > b.per_pallet) {
best = Some(fit);
}
}
best.unwrap()
}
pub fn carton_from_value(v: &Value) -> Carton {
Carton {
length_mm: v.get("lengthMm").as_i64(),
width_mm: v.get("widthMm").as_i64(),
height_mm: v.get("heightMm").as_i64(),
weight_grams: v.get("weightGrams").as_i64(),
}
}
pub fn pallet_fit_to_value(fit: &PalletFit) -> Value {
Value::obj(vec![
("palletLengthMm", Value::Int(fit.pallet_length_mm)),
("palletWidthMm", Value::Int(fit.pallet_width_mm)),
("cartonHeightMm", Value::Int(fit.carton_height_mm)),
("perLayer", Value::Int(fit.per_layer)),
("layers", Value::Int(fit.layers)),
("perPallet", Value::Int(fit.per_pallet)),
("limitedBy", Value::str(&fit.limited_by)),
("loadHeightMm", Value::Int(fit.load_height_mm)),
("loadWeightGrams", Value::Int(fit.load_weight_grams)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
for key in ["lengthMm", "widthMm", "heightMm"] {
if let Value::Float(f) = args[0].get(key) {
panic!("{} must be a whole number greater than zero, received {}", key, f);
}
}
let max_weight = if args[3].is_null() { None } else { Some(args[3].as_i64()) };
pallet_fit_to_value(&pallet_fit(
&carton_from_value(&args[0]),
args[1].as_str(),
args[2].as_i64(),
max_weight,
args[4].as_bool(),
))
}