Functional Weave
Code in Rust

finance.markup-price@1.0.0

impl/rust.rs

1,872 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

const MAX_SAFE: i128 = 9_007_199_254_740_991;

/// The selling price that hits a target margin or markup on `cost`.
///
/// Margin divides by the price and markup by the cost, so the same 5000 basis
/// points means 200.00 or 150.00 on a cost of 100.00; the basis is explicit.
///
/// # Panics
/// Panics on a negative cost, a margin of 100% or more, a markup below -100%,
/// an unknown basis, or a calculation beyond 2^53 - 1.
pub fn markup_price(cost: &Money, basis_points: i64, basis: &str, mode: &str) -> Money {
    if cost.minor < 0 {
        panic!("cost must not be negative, received {}", cost.minor);
    }
    let (numerator, denominator): (i128, i128) = match basis {
        "markup" => {
            if basis_points < -10000 {
                panic!("a markup of {} basis points gives a negative price", basis_points);
            }
            (cost.minor as i128 * (10000 + basis_points as i128), 10000)
        }
        "margin" => {
            if basis_points >= 10000 {
                panic!("a margin of {} basis points has no finite price", basis_points);
            }
            (cost.minor as i128 * 10000, 10000 - basis_points as i128)
        }
        other => panic!("unknown pricing basis \"{}\"", other),
    };
    // i64 could go further, but TypeScript cannot, and all three must agree.
    if numerator > MAX_SAFE {
        panic!("the price calculation exceeds 2^53 - 1");
    }
    money(
        round_div(numerator as i64, denominator as i64, mode),
        &cost.currency,
    )
}

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