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()))
}