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::logistics_container_fit_data::CONTAINERS; ← this capability’s own data, compiled from data/containers.json into the same file by fune build
/// How many identical cartons fit in a container or trailer: the best block
/// stow over the allowed orientations that pass the door, capped by payload.
///
/// # Panics
/// Panics on a dimension or weight below 1, an unknown container, a payload
/// limit above the container's rating, or a carton no orientation fits.
pub fn container_fit(
container: &str,
length_mm: i64,
width_mm: i64,
height_mm: i64,
carton_grams: i64,
keep_upright: bool,
max_payload_kg: Option<i64>,
) -> ContainerFit {
for d in [length_mm, width_mm, height_mm] {
if d < 1 {
panic!("carton dimensions must be 1 mm or more, received {}", d);
}
}
if carton_grams < 1 {
panic!("cartonGrams must be 1 or more, received {}", carton_grams);
}
let spec = match CONTAINERS.iter().find(|c| c.code == container) {
Some(s) => s,
None => {
let codes: Vec<&str> = CONTAINERS.iter().map(|c| c.code).collect();
panic!("unknown container \"{}\": expected {}", container, codes.join(", "));
}
};
let mut payload_kg = spec.max_payload_kg;
if let Some(limit) = max_payload_kg {
if limit < 1 {
panic!("maxPayloadKg must be 1 or more, received {}", limit);
}
if limit > spec.max_payload_kg {
panic!(
"maxPayloadKg must not exceed {} kg for {}, received {}",
spec.max_payload_kg, container, limit
);
}
payload_kg = limit;
}
let (l, w, h) = (length_mm, width_mm, height_mm);
// Upright first (as given, then turned on the floor), then the four tipped ones.
let orientations: Vec<(i64, i64, i64)> = if keep_upright {
vec![(l, w, h), (w, l, h)]
} else {
vec![(l, w, h), (w, l, h), (l, h, w), (h, l, w), (w, h, l), (h, w, l)]
};
let mut best: Option<(i64, i64, i64, (i64, i64, i64))> = None;
let mut best_count = 0;
for o in orientations {
let (a, b, c) = o;
if a > spec.length_mm || b > spec.width_mm || c > spec.height_mm {
continue;
}
// End-loaded containers: the carton's cross-section has to pass the door.
if let Some(door) = spec.door_width_mm {
if b > door {
continue;
}
}
if let Some(door) = spec.door_height_mm {
if c > door {
continue;
}
}
let along = spec.length_mm / a;
let across = spec.width_mm / b;
let layers = spec.height_mm / c;
let count = along * across * layers;
if best.is_none() || count > best_count {
best = Some((along, across, layers, o));
best_count = count;
}
}
let (along, across, layers, placed) = match best {
Some(b) => b,
None => panic!("carton does not fit in {} in any allowed orientation", container),
};
let weight_limit = payload_kg * 1000 / carton_grams;
let cartons = best_count.min(weight_limit);
let space = spec.length_mm * spec.width_mm * spec.height_mm;
ContainerFit {
container: container.to_string(),
cartons,
space_limit: best_count,
weight_limit,
limited_by: if weight_limit < best_count { "weight" } else { "space" }.to_string(),
along_length: along,
across_width: across,
layers,
placed_length_mm: placed.0,
placed_width_mm: placed.1,
placed_height_mm: placed.2,
cargo_grams: cartons * carton_grams,
volume_used_basis_points: cartons * l * w * h * 10000 / space,
}
}
pub fn container_fit_to_value(f: &ContainerFit) -> Value {
Value::obj(vec![
("container", Value::str(&f.container)),
("cartons", Value::Int(f.cartons)),
("spaceLimit", Value::Int(f.space_limit)),
("weightLimit", Value::Int(f.weight_limit)),
("limitedBy", Value::str(&f.limited_by)),
("alongLength", Value::Int(f.along_length)),
("acrossWidth", Value::Int(f.across_width)),
("layers", Value::Int(f.layers)),
("placedLengthMm", Value::Int(f.placed_length_mm)),
("placedWidthMm", Value::Int(f.placed_width_mm)),
("placedHeightMm", Value::Int(f.placed_height_mm)),
("cargoGrams", Value::Int(f.cargo_grams)),
("volumeUsedBasisPoints", Value::Int(f.volume_used_basis_points)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
let limit = if args[6].is_null() { None } else { Some(args[6].as_i64()) };
container_fit_to_value(&container_fit(
args[0].as_str(),
args[1].as_i64(),
args[2].as_i64(),
args[3].as_i64(),
args[4].as_i64(),
args[5].as_bool(),
limit,
))
}