Functional Weave
Code in Rust

logistics.route-distance@1.0.0

impl/rust.rs

1,698 bytes · the Rust implementation · view raw

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::geo_distance::distance;  ← from geo.distance ^1.0.0 · built alongside by fune
use super::geo_point_in_polygon::{geo_point_from_value, GeoPoint};  ← from geo.point-in-polygon ^1.0.0 · built alongside by fune

/// Great-circle length of a route through the stops in order, leg by leg,
/// optionally closing back to the first stop.
///
/// # Panics
/// Panics on an empty list or a coordinate out of range.
pub fn route_distance(stops: &[GeoPoint], return_to_start: bool) -> RouteDistance {
    if stops.is_empty() {
        panic!("stops must not be empty");
    }
    let mut leg_metres: Vec<f64> = Vec::new();
    // Each leg is a whole number of millimetres; adding them as integers and
    // dividing once avoids floating-point drift (and is the same double everywhere).
    let mut total_millimetres: i64 = 0;
    let mut add_leg = |a: &GeoPoint, b: &GeoPoint| {
        let metres = distance(a.lat, a.lng, b.lat, b.lng);
        leg_metres.push(metres);
        total_millimetres += (metres * 1000.0 + 0.5).floor() as i64;
    };
    for i in 1..stops.len() {
        add_leg(&stops[i - 1], &stops[i]);
    }
    if return_to_start && stops.len() > 1 {
        add_leg(&stops[stops.len() - 1], &stops[0]);
    }
    RouteDistance {
        total_metres: total_millimetres as f64 / 1000.0,
        leg_metres,
    }
}

pub fn route_distance_to_value(r: &RouteDistance) -> Value {
    Value::obj(vec![
        ("totalMetres", Value::Float(r.total_metres)),
        ("legMetres", Value::Arr(r.leg_metres.iter().map(|m| Value::Float(*m)).collect())),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let stops: Vec<GeoPoint> = args[0].as_arr().iter().map(geo_point_from_value).collect();
    route_distance_to_value(&route_distance(&stops, args[1].as_bool()))
}