Functional Weave
Code in Rust

retail.price-rounding@1.0.0

impl/rust.rs

1,855 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::math_round_div::round_div;  ← from math.round-div ^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

/// Snap a price to the points k x step - ending.
///
/// Shifting by `ending` turns every point into a multiple of `step`, so the
/// whole policy is one integer division with the right rounding mode.
///
/// # Panics
/// Panics on a step below 1, an ending outside 0..step, a negative price,
/// `down` below the first point, or an unknown direction.
pub fn round_price(price: &Money, step: i64, ending: i64, direction: &str) -> Money {
    if step < 1 {
        panic!("step must be 1 or more, received {}", step);
    }
    if ending < 0 || ending >= step {
        panic!("ending must be from 0 to step - 1, received {}", ending);
    }
    if price.minor < 0 {
        panic!("price must not be negative, received {}", price.minor);
    }
    let shifted = price.minor + ending;
    let point = match direction {
        "up" => round_div(shifted, step, "up") * step - ending,
        "down" => {
            let point = round_div(shifted, step, "down") * step - ending;
            if point < 0 {
                panic!("no price point at or below {}", price.minor);
            }
            point
        }
        "nearest" => {
            let point = round_div(shifted, step, "half-up") * step - ending;
            // Below the first point the only candidate is above.
            if point < 0 {
                step - ending
            } else {
                point
            }
        }
        other => panic!("unknown direction \"{}\"", other),
    };
    money(point, &price.currency)
}

pub fn fune_vector(args: &[Value]) -> Value {
    money_to_value(&round_price(
        &money_from_value(&args[0]),
        args[1].as_i64(),
        args[2].as_i64(),
        args[3].as_str(),
    ))
}