Functional Weave
Code in Rust

energy.carbon-emissions@1.0.1

impl/rust.rs

2,236 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::energy_carbon_emissions_data::CONVERSION_FACTORS;  ← this capability’s own data, compiled from data/conversion-factors.json into the same file by fune build
use super::math_round_div::round_div;  ← from math.round-div ^1.0.0 · built alongside by fune

// quantity (thousandths of a unit) x factor (hundred-thousandths of a kg)
// / 100000 is grams.
const DIVISOR: i64 = 100_000;
const MAX_SAFE: i128 = 9_007_199_254_740_991;

/// kgCO2e, as whole grams, for an amount of a fuel, from the UK government
/// conversion factor set for `year`.
///
/// # Panics
/// Panics on a negative quantity, an unknown fuel, a year with no factor set,
/// or a result too large to be exact in every language.
pub fn carbon_emissions(fuel: &str, quantity: i64, year: i64) -> CarbonEmissions {
    if quantity < 0 {
        panic!(
            "quantity must be a whole number of thousandths of a unit, 0 or more, received {}",
            quantity
        );
    }
    if !CONVERSION_FACTORS.iter().any(|f| f.fuel == fuel) {
        panic!("unknown fuel \"{}\"", fuel);
    }
    let row = match CONVERSION_FACTORS.iter().find(|f| f.fuel == fuel && f.year == year) {
        Some(row) => row,
        None => panic!("no conversion factor for {} in {}", fuel, year),
    };
    let whole = quantity / DIVISOR;
    let rest = quantity % DIVISOR;
    let grams = whole as i128 * row.factor as i128 + round_div(rest * row.factor, DIVISOR, "half-up") as i128;
    if grams > MAX_SAFE {
        panic!("emissions too large to calculate exactly");
    }
    CarbonEmissions {
        fuel: fuel.to_string(),
        unit: row.unit.to_string(),
        year,
        scope: row.scope,
        factor: row.factor,
        grams: grams as i64,
    }
}

pub fn carbon_emissions_to_value(e: &CarbonEmissions) -> Value {
    Value::obj(vec![
        ("fuel", Value::str(&e.fuel)),
        ("unit", Value::str(&e.unit)),
        ("year", Value::Int(e.year)),
        ("scope", Value::Int(e.scope)),
        ("factor", Value::Int(e.factor)),
        ("grams", Value::Int(e.grams)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    if let Value::Float(f) = &args[1] {
        panic!("quantity must be a whole number of thousandths of a unit, 0 or more, received {}", f);
    }
    carbon_emissions_to_value(&carbon_emissions(args[0].as_str(), args[1].as_i64(), args[2].as_i64()))
}