const ENERGY_PER_IO_OP_KWH: f64 = 0.000_000_1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Provider {
Aws,
Gcp,
Azure,
Generic,
}
impl Provider {
const fn pue(self) -> f64 {
match self {
Self::Aws => 1.135,
Self::Gcp => 1.10,
Self::Azure => 1.185,
Self::Generic => 1.2,
}
}
}
static CARBON_TABLE: &[(&str, f64, Provider)] = &[
("us-east-1", 379.0, Provider::Aws),
("us-east-2", 410.0, Provider::Aws),
("us-west-1", 200.0, Provider::Aws),
("us-west-2", 89.0, Provider::Aws),
("eu-west-1", 296.0, Provider::Aws), ("eu-west-2", 231.0, Provider::Aws), ("eu-west-3", 56.0, Provider::Aws), ("eu-central-1", 338.0, Provider::Aws), ("eu-north-1", 8.0, Provider::Aws), ("ap-northeast-1", 462.0, Provider::Aws), ("ap-southeast-1", 408.0, Provider::Aws), ("ap-southeast-2", 550.0, Provider::Aws), ("ap-south-1", 708.0, Provider::Aws), ("ca-central-1", 13.0, Provider::Aws), ("sa-east-1", 62.0, Provider::Aws), ("us-central1", 426.0, Provider::Gcp),
("us-east1", 379.0, Provider::Gcp),
("us-west1", 89.0, Provider::Gcp),
("europe-west1", 187.0, Provider::Gcp), ("europe-west4", 328.0, Provider::Gcp), ("europe-west9", 56.0, Provider::Gcp), ("europe-north1", 8.0, Provider::Gcp), ("asia-northeast1", 462.0, Provider::Gcp), ("eastus", 379.0, Provider::Azure),
("westus2", 89.0, Provider::Azure),
("westeurope", 328.0, Provider::Azure), ("northeurope", 296.0, Provider::Azure), ("francecentral", 56.0, Provider::Azure),
("uksouth", 231.0, Provider::Azure),
("fr", 56.0, Provider::Generic),
("de", 338.0, Provider::Generic),
("gb", 231.0, Provider::Generic),
("uk", 231.0, Provider::Generic),
("us", 379.0, Provider::Generic),
("ie", 296.0, Provider::Generic),
("se", 8.0, Provider::Generic),
("no", 7.0, Provider::Generic),
("ca", 13.0, Provider::Generic),
("jp", 462.0, Provider::Generic),
("in", 708.0, Provider::Generic),
("au", 550.0, Provider::Generic),
("br", 62.0, Provider::Generic),
("sg", 408.0, Provider::Generic),
("nl", 328.0, Provider::Generic),
("be", 187.0, Provider::Generic),
("fi", 8.0, Provider::Generic),
];
static REGION_MAP: std::sync::LazyLock<std::collections::HashMap<&'static str, (f64, Provider)>> =
std::sync::LazyLock::new(|| {
CARBON_TABLE
.iter()
.map(|&(key, intensity, provider)| (key, (intensity, provider)))
.collect()
});
#[must_use]
pub fn lookup_region(region: &str) -> Option<(f64, f64)> {
let lower = region.to_ascii_lowercase();
lookup_region_lower(&lower)
}
#[must_use]
fn lookup_region_lower(region: &str) -> Option<(f64, f64)> {
REGION_MAP
.get(region)
.map(|(intensity, provider)| (*intensity, provider.pue()))
}
#[must_use]
pub fn io_ops_to_co2_grams(io_ops: usize, region: &str) -> Option<f64> {
let (intensity, pue) = lookup_region_lower(region)?;
Some(io_ops as f64 * ENERGY_PER_IO_OP_KWH * intensity * pue)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookup_known_aws_region() {
let result = lookup_region("eu-west-3");
assert!(result.is_some());
let (intensity, pue) = result.unwrap();
assert!((intensity - 56.0).abs() < f64::EPSILON);
assert!((pue - 1.135).abs() < f64::EPSILON);
}
#[test]
fn lookup_known_gcp_region() {
let result = lookup_region("europe-west9");
assert!(result.is_some());
let (intensity, pue) = result.unwrap();
assert!((intensity - 56.0).abs() < f64::EPSILON);
assert!((pue - 1.10).abs() < f64::EPSILON);
}
#[test]
fn lookup_country_code() {
let result = lookup_region("FR");
assert!(result.is_some());
let (intensity, pue) = result.unwrap();
assert!((intensity - 56.0).abs() < f64::EPSILON);
assert!((pue - 1.2).abs() < f64::EPSILON);
}
#[test]
fn lookup_case_insensitive() {
assert!(lookup_region("EU-WEST-3").is_some());
assert!(lookup_region("Us-East-1").is_some());
assert!(lookup_region("fr").is_some());
assert!(lookup_region("FR").is_some());
}
#[test]
fn lookup_unknown_region_returns_none() {
assert!(lookup_region("unknown-region").is_none());
assert!(lookup_region("").is_none());
}
#[test]
fn io_ops_to_co2_known_region() {
let co2 = io_ops_to_co2_grams(1000, "eu-west-3");
assert!(co2.is_some());
let val = co2.unwrap();
assert!((val - 0.006_356).abs() < 1e-9);
}
#[test]
fn io_ops_to_co2_unknown_region() {
assert!(io_ops_to_co2_grams(1000, "mars-1").is_none());
}
#[test]
fn io_ops_to_co2_zero_ops() {
let co2 = io_ops_to_co2_grams(0, "eu-west-3");
assert!(co2.is_some());
assert!((co2.unwrap() - 0.0).abs() < f64::EPSILON);
}
#[test]
fn high_carbon_region_vs_low() {
let high = io_ops_to_co2_grams(1000, "ap-south-1").unwrap(); let low = io_ops_to_co2_grams(1000, "eu-north-1").unwrap(); assert!(high > low * 10.0, "India should be much higher than Sweden");
}
#[test]
fn lookup_azure_region() {
let result = lookup_region("eastus");
assert!(result.is_some());
let (_, pue) = result.unwrap();
assert!(
(pue - 1.185).abs() < f64::EPSILON,
"Azure PUE should be 1.185"
);
}
}