#![allow(dead_code, unused_imports, unused_variables)]
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
static EMISSION_COUNTER: AtomicU64 = AtomicU64::new(0);
static REPORT_COUNTER: AtomicU64 = AtomicU64::new(0);
fn generate_emission_id() -> String {
format!(
"emission-{}",
EMISSION_COUNTER.fetch_add(1, Ordering::SeqCst)
)
}
fn generate_report_id() -> String {
format!("report-{}", REPORT_COUNTER.fetch_add(1, Ordering::SeqCst))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EmissionSource {
LlmApiCall,
GpuCompute,
CpuCompute,
DataTransfer,
Storage,
Network,
Build,
Container,
Database,
Other,
}
impl std::fmt::Display for EmissionSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EmissionSource::LlmApiCall => write!(f, "LLM API"),
EmissionSource::GpuCompute => write!(f, "GPU Compute"),
EmissionSource::CpuCompute => write!(f, "CPU Compute"),
EmissionSource::DataTransfer => write!(f, "Data Transfer"),
EmissionSource::Storage => write!(f, "Storage"),
EmissionSource::Network => write!(f, "Network"),
EmissionSource::Build => write!(f, "Build"),
EmissionSource::Container => write!(f, "Container"),
EmissionSource::Database => write!(f, "Database"),
EmissionSource::Other => write!(f, "Other"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GridIntensity {
VeryLow,
Low,
Medium,
High,
VeryHigh,
Custom(f64),
}
impl GridIntensity {
pub fn grams_co2_per_kwh(&self) -> f64 {
match self {
GridIntensity::VeryLow => 20.0, GridIntensity::Low => 50.0, GridIntensity::Medium => 250.0, GridIntensity::High => 400.0, GridIntensity::VeryHigh => 600.0, GridIntensity::Custom(v) => *v,
}
}
}
impl std::fmt::Display for GridIntensity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GridIntensity::VeryLow => write!(f, "Very Low (~20g CO2e/kWh)"),
GridIntensity::Low => write!(f, "Low (~50g CO2e/kWh)"),
GridIntensity::Medium => write!(f, "Medium (~250g CO2e/kWh)"),
GridIntensity::High => write!(f, "High (~400g CO2e/kWh)"),
GridIntensity::VeryHigh => write!(f, "Very High (~600g CO2e/kWh)"),
GridIntensity::Custom(v) => write!(f, "Custom ({}g CO2e/kWh)", v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CloudProvider {
Aws,
Gcp,
Azure,
SelfHosted,
Local,
Other,
}
impl std::fmt::Display for CloudProvider {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CloudProvider::Aws => write!(f, "AWS"),
CloudProvider::Gcp => write!(f, "GCP"),
CloudProvider::Azure => write!(f, "Azure"),
CloudProvider::SelfHosted => write!(f, "Self-Hosted"),
CloudProvider::Local => write!(f, "Local"),
CloudProvider::Other => write!(f, "Other"),
}
}
}
impl CloudProvider {
pub fn pue(&self) -> f64 {
match self {
CloudProvider::Gcp => 1.1, CloudProvider::Aws => 1.2, CloudProvider::Azure => 1.18, CloudProvider::SelfHosted => 1.6, CloudProvider::Local => 2.0, CloudProvider::Other => 1.5, }
}
pub fn has_green_option(&self) -> bool {
match self {
CloudProvider::Gcp => true, CloudProvider::Aws => true, CloudProvider::Azure => true, CloudProvider::SelfHosted | CloudProvider::Local | CloudProvider::Other => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LlmModel {
GptLarge,
GptMedium,
Small,
Tiny,
Local,
Claude,
Custom,
}
impl std::fmt::Display for LlmModel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LlmModel::GptLarge => write!(f, "GPT-4/Large"),
LlmModel::GptMedium => write!(f, "GPT-3.5/Medium"),
LlmModel::Small => write!(f, "Small (7B)"),
LlmModel::Tiny => write!(f, "Tiny (<3B)"),
LlmModel::Local => write!(f, "Local"),
LlmModel::Claude => write!(f, "Claude"),
LlmModel::Custom => write!(f, "Custom"),
}
}
}
impl LlmModel {
pub fn wh_per_1k_tokens(&self) -> f64 {
match self {
LlmModel::GptLarge => 0.5, LlmModel::GptMedium => 0.1, LlmModel::Small => 0.05, LlmModel::Tiny => 0.01, LlmModel::Local => 0.1, LlmModel::Claude => 0.3, LlmModel::Custom => 0.2, }
}
}
#[derive(Debug, Clone)]
pub struct EmissionRecord {
pub id: String,
pub source: EmissionSource,
pub co2e_grams: f64,
pub energy_wh: f64,
pub timestamp: u64,
pub duration: Option<Duration>,
pub description: String,
pub operation: Option<String>,
pub provider: Option<CloudProvider>,
pub region: Option<String>,
}
impl EmissionRecord {
pub fn new(source: EmissionSource, co2e_grams: f64) -> Self {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
Self {
id: generate_emission_id(),
source,
co2e_grams,
energy_wh: 0.0,
timestamp,
duration: None,
description: String::new(),
operation: None,
provider: None,
region: None,
}
}
pub fn with_energy(mut self, energy_wh: f64) -> Self {
self.energy_wh = energy_wh;
self
}
pub fn with_duration(mut self, duration: Duration) -> Self {
self.duration = Some(duration);
self
}
pub fn with_description(mut self, desc: impl Into<String>) -> Self {
self.description = desc.into();
self
}
pub fn with_operation(mut self, op: impl Into<String>) -> Self {
self.operation = Some(op.into());
self
}
pub fn with_provider(mut self, provider: CloudProvider) -> Self {
self.provider = Some(provider);
self
}
pub fn with_region(mut self, region: impl Into<String>) -> Self {
self.region = Some(region.into());
self
}
}
#[derive(Debug, Clone)]
pub struct Optimization {
pub title: String,
pub description: String,
pub estimated_savings_grams: f64,
pub effort: EffortLevel,
pub priority: Priority,
pub category: OptimizationCategory,
}
impl Optimization {
pub fn new(title: impl Into<String>, description: impl Into<String>) -> Self {
Self {
title: title.into(),
description: description.into(),
estimated_savings_grams: 0.0,
effort: EffortLevel::Low,
priority: Priority::Medium,
category: OptimizationCategory::Compute,
}
}
pub fn with_savings(mut self, grams: f64) -> Self {
self.estimated_savings_grams = grams;
self
}
pub fn with_effort(mut self, effort: EffortLevel) -> Self {
self.effort = effort;
self
}
pub fn with_priority(mut self, priority: Priority) -> Self {
self.priority = priority;
self
}
pub fn with_category(mut self, category: OptimizationCategory) -> Self {
self.category = category;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum EffortLevel {
Low,
Medium,
High,
}
impl std::fmt::Display for EffortLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EffortLevel::Low => write!(f, "Low"),
EffortLevel::Medium => write!(f, "Medium"),
EffortLevel::High => write!(f, "High"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Priority {
Low,
Medium,
High,
Critical,
}
impl std::fmt::Display for Priority {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Priority::Low => write!(f, "Low"),
Priority::Medium => write!(f, "Medium"),
Priority::High => write!(f, "High"),
Priority::Critical => write!(f, "Critical"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OptimizationCategory {
Compute,
Api,
Storage,
Network,
Hosting,
Caching,
ModelSelection,
}
impl std::fmt::Display for OptimizationCategory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OptimizationCategory::Compute => write!(f, "Compute"),
OptimizationCategory::Api => write!(f, "API"),
OptimizationCategory::Storage => write!(f, "Storage"),
OptimizationCategory::Network => write!(f, "Network"),
OptimizationCategory::Hosting => write!(f, "Hosting"),
OptimizationCategory::Caching => write!(f, "Caching"),
OptimizationCategory::ModelSelection => write!(f, "Model Selection"),
}
}
}
#[derive(Debug, Clone)]
pub struct GreenHosting {
pub provider: String,
pub region: String,
pub grid_intensity: GridIntensity,
pub renewable_percentage: u8,
pub carbon_neutral: bool,
pub description: String,
}
impl GreenHosting {
pub fn new(provider: impl Into<String>, region: impl Into<String>) -> Self {
Self {
provider: provider.into(),
region: region.into(),
grid_intensity: GridIntensity::Medium,
renewable_percentage: 0,
carbon_neutral: false,
description: String::new(),
}
}
pub fn with_intensity(mut self, intensity: GridIntensity) -> Self {
self.grid_intensity = intensity;
self
}
pub fn with_renewable(mut self, percentage: u8) -> Self {
self.renewable_percentage = percentage.min(100);
self
}
pub fn carbon_neutral(mut self) -> Self {
self.carbon_neutral = true;
self
}
pub fn with_description(mut self, desc: impl Into<String>) -> Self {
self.description = desc.into();
self
}
}
#[derive(Debug)]
pub struct EmissionCalculator {
grid_intensity: GridIntensity,
provider: CloudProvider,
}
impl Default for EmissionCalculator {
fn default() -> Self {
Self::new()
}
}
impl EmissionCalculator {
pub fn new() -> Self {
Self {
grid_intensity: GridIntensity::Medium,
provider: CloudProvider::Local,
}
}
pub fn with_intensity(mut self, intensity: GridIntensity) -> Self {
self.grid_intensity = intensity;
self
}
pub fn with_provider(mut self, provider: CloudProvider) -> Self {
self.provider = provider;
self
}
pub fn energy_to_co2e(&self, energy_wh: f64) -> f64 {
let adjusted_energy = energy_wh * self.provider.pue();
(adjusted_energy / 1000.0) * self.grid_intensity.grams_co2_per_kwh()
}
pub fn llm_call_emission(&self, model: LlmModel, tokens: u64) -> EmissionRecord {
let energy_wh = model.wh_per_1k_tokens() * (tokens as f64 / 1000.0);
let co2e = self.energy_to_co2e(energy_wh);
EmissionRecord::new(EmissionSource::LlmApiCall, co2e)
.with_energy(energy_wh)
.with_description(format!("{} API call with {} tokens", model, tokens))
}
pub fn cpu_emission(&self, duration: Duration, cpu_power_w: f64) -> EmissionRecord {
let hours = duration.as_secs_f64() / 3600.0;
let energy_wh = cpu_power_w * hours;
let co2e = self.energy_to_co2e(energy_wh);
EmissionRecord::new(EmissionSource::CpuCompute, co2e)
.with_energy(energy_wh)
.with_duration(duration)
.with_description(format!(
"CPU compute at {}W for {:?}",
cpu_power_w, duration
))
}
pub fn gpu_emission(&self, duration: Duration, gpu_power_w: f64) -> EmissionRecord {
let hours = duration.as_secs_f64() / 3600.0;
let energy_wh = gpu_power_w * hours;
let co2e = self.energy_to_co2e(energy_wh);
EmissionRecord::new(EmissionSource::GpuCompute, co2e)
.with_energy(energy_wh)
.with_duration(duration)
.with_description(format!(
"GPU compute at {}W for {:?}",
gpu_power_w, duration
))
}
pub fn data_transfer_emission(&self, bytes: u64) -> EmissionRecord {
let gb = bytes as f64 / (1024.0 * 1024.0 * 1024.0);
let energy_wh = gb * 60.0; let co2e = self.energy_to_co2e(energy_wh);
EmissionRecord::new(EmissionSource::DataTransfer, co2e)
.with_energy(energy_wh)
.with_description(format!("Data transfer: ~{:.2} GB", gb))
}
pub fn storage_emission(&self, gb_months: f64) -> EmissionRecord {
let energy_wh = gb_months * 700.0; let co2e = self.energy_to_co2e(energy_wh);
EmissionRecord::new(EmissionSource::Storage, co2e)
.with_energy(energy_wh)
.with_description(format!("Storage: ~{:.2} GB-months", gb_months))
}
pub fn build_emission(
&self,
duration: Duration,
cpu_cores: u32,
core_power_w: f64,
) -> EmissionRecord {
let hours = duration.as_secs_f64() / 3600.0;
let total_power = cpu_cores as f64 * core_power_w;
let energy_wh = total_power * hours;
let co2e = self.energy_to_co2e(energy_wh);
EmissionRecord::new(EmissionSource::Build, co2e)
.with_energy(energy_wh)
.with_duration(duration)
.with_description(format!("Build with {} cores for {:?}", cpu_cores, duration))
}
}
#[derive(Debug)]
pub struct CarbonTracker {
records: Vec<EmissionRecord>,
calculator: EmissionCalculator,
_session_start: u64,
provider: CloudProvider,
region: Option<String>,
}
impl Default for CarbonTracker {
fn default() -> Self {
Self::new()
}
}
impl CarbonTracker {
pub fn new() -> Self {
let _session_start = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
Self {
records: Vec::new(),
calculator: EmissionCalculator::new(),
_session_start,
provider: CloudProvider::Local,
region: None,
}
}
pub fn with_calculator(mut self, calculator: EmissionCalculator) -> Self {
self.calculator = calculator;
self
}
pub fn with_provider(mut self, provider: CloudProvider) -> Self {
self.provider = provider;
self.calculator = self.calculator.with_provider(provider);
self
}
pub fn with_region(mut self, region: impl Into<String>) -> Self {
self.region = Some(region.into());
self
}
pub fn record(&mut self, record: EmissionRecord) {
self.records.push(record);
}
pub fn track_llm_call(&mut self, model: LlmModel, tokens: u64) {
let record = self
.calculator
.llm_call_emission(model, tokens)
.with_provider(self.provider);
self.record(record);
}
pub fn track_cpu(&mut self, duration: Duration, power_w: f64) {
let record = self
.calculator
.cpu_emission(duration, power_w)
.with_provider(self.provider);
self.record(record);
}
pub fn track_gpu(&mut self, duration: Duration, power_w: f64) {
let record = self
.calculator
.gpu_emission(duration, power_w)
.with_provider(self.provider);
self.record(record);
}
pub fn track_data_transfer(&mut self, bytes: u64) {
let record = self
.calculator
.data_transfer_emission(bytes)
.with_provider(self.provider);
self.record(record);
}
pub fn total_co2e(&self) -> f64 {
self.records.iter().map(|r| r.co2e_grams).sum()
}
pub fn total_energy(&self) -> f64 {
self.records.iter().map(|r| r.energy_wh).sum()
}
pub fn by_source(&self) -> HashMap<EmissionSource, f64> {
let mut result = HashMap::new();
for record in &self.records {
*result.entry(record.source).or_insert(0.0) += record.co2e_grams;
}
result
}
pub fn record_count(&self) -> usize {
self.records.len()
}
pub fn records(&self) -> &[EmissionRecord] {
&self.records
}
pub fn suggest_optimizations(&self) -> Vec<Optimization> {
let mut suggestions = Vec::new();
let by_source = self.by_source();
if let Some(&llm_co2) = by_source.get(&EmissionSource::LlmApiCall) {
if llm_co2 > 100.0 {
suggestions.push(
Optimization::new(
"Use smaller models for simple tasks",
"Consider using GPT-3.5 or smaller models for tasks that don't require GPT-4 level capabilities",
)
.with_savings(llm_co2 * 0.5)
.with_category(OptimizationCategory::ModelSelection)
.with_priority(Priority::High)
);
suggestions.push(
Optimization::new(
"Implement response caching",
"Cache responses for repeated queries to avoid redundant API calls",
)
.with_savings(llm_co2 * 0.3)
.with_category(OptimizationCategory::Caching)
.with_effort(EffortLevel::Medium),
);
}
}
let compute_co2 = by_source.get(&EmissionSource::CpuCompute).unwrap_or(&0.0)
+ by_source.get(&EmissionSource::GpuCompute).unwrap_or(&0.0);
if compute_co2 > 50.0 {
suggestions.push(
Optimization::new(
"Schedule compute during low-carbon hours",
"Run batch jobs during off-peak hours when renewable energy is more available",
)
.with_savings(compute_co2 * 0.2)
.with_category(OptimizationCategory::Compute)
.with_effort(EffortLevel::Medium),
);
}
if let Some(&transfer_co2) = by_source.get(&EmissionSource::DataTransfer) {
if transfer_co2 > 10.0 {
suggestions.push(
Optimization::new(
"Compress data transfers",
"Use gzip or brotli compression to reduce data transfer volume",
)
.with_savings(transfer_co2 * 0.6)
.with_category(OptimizationCategory::Network)
.with_effort(EffortLevel::Low),
);
}
}
if !self.provider.has_green_option() {
suggestions.push(
Optimization::new(
"Switch to green cloud provider",
"Consider using GCP (carbon neutral) or AWS/Azure with renewable energy options",
)
.with_savings(self.total_co2e() * 0.8)
.with_category(OptimizationCategory::Hosting)
.with_effort(EffortLevel::High)
.with_priority(Priority::Critical)
);
}
suggestions
}
pub fn green_hosting_recommendations(&self) -> Vec<GreenHosting> {
vec![
GreenHosting::new("GCP", "us-central1")
.with_intensity(GridIntensity::Low)
.with_renewable(100)
.carbon_neutral()
.with_description("Carbon neutral since 2007, 100% renewable energy matching"),
GreenHosting::new("AWS", "eu-north-1")
.with_intensity(GridIntensity::VeryLow)
.with_renewable(100)
.with_description("Stockholm region runs on 100% renewable energy"),
GreenHosting::new("Azure", "Sweden Central")
.with_intensity(GridIntensity::VeryLow)
.with_renewable(100)
.with_description("Swedish data centers powered by renewable energy"),
GreenHosting::new("AWS", "us-west-2")
.with_intensity(GridIntensity::Low)
.with_renewable(95)
.with_description("Oregon region with high renewable energy mix"),
]
}
pub fn generate_report(&self) -> CarbonReport {
CarbonReport::new(self)
}
}
#[derive(Debug)]
pub struct CarbonReport {
pub id: String,
pub total_co2e_grams: f64,
pub total_energy_wh: f64,
pub by_source: HashMap<EmissionSource, f64>,
pub record_count: usize,
pub optimizations: Vec<Optimization>,
pub green_options: Vec<GreenHosting>,
pub equivalents: CarbonEquivalents,
}
impl CarbonReport {
pub fn new(tracker: &CarbonTracker) -> Self {
let total_co2e_grams = tracker.total_co2e();
Self {
id: generate_report_id(),
total_co2e_grams,
total_energy_wh: tracker.total_energy(),
by_source: tracker.by_source(),
record_count: tracker.record_count(),
optimizations: tracker.suggest_optimizations(),
green_options: tracker.green_hosting_recommendations(),
equivalents: CarbonEquivalents::from_co2e_grams(total_co2e_grams),
}
}
pub fn to_markdown(&self) -> String {
let mut output = String::new();
output.push_str("# Carbon Footprint Report\n\n");
output.push_str(
"> **Note:** Carbon estimates are approximate, based on average \
cloud GPU power consumption data and publicly available research. \
Actual emissions depend on hardware, grid region, time of day, and \
provider-specific efficiency. Use these figures for directional \
awareness, not precise accounting.\n\n",
);
output.push_str("## Summary\n\n");
output.push_str(&format!(
"- **Estimated Total CO2e**: ~{:.2}g (~{:.4} kg)\n",
self.total_co2e_grams,
self.total_co2e_grams / 1000.0
));
output.push_str(&format!(
"- **Estimated Total Energy**: ~{:.2} Wh (~{:.4} kWh)\n",
self.total_energy_wh,
self.total_energy_wh / 1000.0
));
output.push_str(&format!(
"- **Operations Tracked**: {}\n\n",
self.record_count
));
output.push_str("## Environmental Impact Context (approximate)\n\n");
output.push_str(&format!(
"- ~{} km driven in a car\n",
self.equivalents.car_km
));
output.push_str(&format!(
"- ~{} smartphone charges\n",
self.equivalents.smartphone_charges
));
output.push_str(&format!(
"- ~{} hours of laptop use\n",
self.equivalents.laptop_hours
));
output.push_str(&format!(
"- ~{} liters of water heated\n",
self.equivalents.liters_water_heated
));
output.push('\n');
output.push_str("## Emissions by Source (estimated)\n\n");
let mut sources: Vec<_> = self.by_source.iter().collect();
sources.sort_by(|a, b| b.1.partial_cmp(a.1).unwrap_or(std::cmp::Ordering::Equal));
for (source, co2e) in sources {
let percentage = if self.total_co2e_grams > 0.0 {
(co2e / self.total_co2e_grams) * 100.0
} else {
0.0
};
output.push_str(&format!(
"- **{}**: ~{:.2}g (~{:.1}%)\n",
source, co2e, percentage
));
}
output.push('\n');
if !self.optimizations.is_empty() {
output.push_str("## Optimization Suggestions\n\n");
for opt in &self.optimizations {
output.push_str(&format!("### {} [{}]\n\n", opt.title, opt.priority));
output.push_str(&format!("{}\n\n", opt.description));
output.push_str(&format!("- **Category**: {}\n", opt.category));
output.push_str(&format!("- **Effort**: {}\n", opt.effort));
output.push_str(&format!(
"- **Est. Savings**: ~{:.2}g CO2e\n\n",
opt.estimated_savings_grams
));
}
}
output.push_str("## Green Hosting Options\n\n");
for hosting in &self.green_options {
output.push_str(&format!(
"### {} - {}\n\n",
hosting.provider, hosting.region
));
if hosting.carbon_neutral {
output.push_str("*Carbon Neutral*\n\n");
}
output.push_str(&format!(
"- Renewable Energy: {}%\n",
hosting.renewable_percentage
));
output.push_str(&format!("- Grid Intensity: {}\n", hosting.grid_intensity));
if !hosting.description.is_empty() {
output.push_str(&format!("\n{}\n\n", hosting.description));
}
}
output
}
}
#[derive(Debug, Clone)]
pub struct CarbonEquivalents {
pub car_km: f64,
pub smartphone_charges: u32,
pub laptop_hours: f64,
pub liters_water_heated: f64,
}
impl CarbonEquivalents {
pub fn from_co2e_grams(grams: f64) -> Self {
Self {
car_km: (grams / 120.0 * 100.0).round() / 100.0,
smartphone_charges: (grams / 8.0).ceil() as u32,
laptop_hours: (grams / 40.0 * 100.0).round() / 100.0,
liters_water_heated: (grams / 50.0 * 100.0).round() / 100.0,
}
}
}
#[derive(Debug)]
pub struct CarbonBudget {
pub daily_grams: f64,
pub weekly_grams: f64,
pub monthly_grams: f64,
pub used_today: f64,
pub used_week: f64,
pub used_month: f64,
}
impl CarbonBudget {
pub fn new(daily_grams: f64) -> Self {
Self {
daily_grams,
weekly_grams: daily_grams * 7.0,
monthly_grams: daily_grams * 30.0,
used_today: 0.0,
used_week: 0.0,
used_month: 0.0,
}
}
pub fn add_usage(&mut self, grams: f64) {
self.used_today += grams;
self.used_week += grams;
self.used_month += grams;
}
pub fn over_daily(&self) -> bool {
self.used_today > self.daily_grams
}
pub fn daily_percentage(&self) -> f64 {
if self.daily_grams > 0.0 {
(self.used_today / self.daily_grams) * 100.0
} else {
0.0
}
}
pub fn remaining_today(&self) -> f64 {
(self.daily_grams - self.used_today).max(0.0)
}
pub fn reset_daily(&mut self) {
self.used_today = 0.0;
}
pub fn reset_weekly(&mut self) {
self.used_week = 0.0;
}
pub fn reset_monthly(&mut self) {
self.used_month = 0.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_emission_source_display() {
assert_eq!(format!("{}", EmissionSource::LlmApiCall), "LLM API");
assert_eq!(format!("{}", EmissionSource::GpuCompute), "GPU Compute");
}
#[test]
fn test_grid_intensity_values() {
assert_eq!(GridIntensity::VeryLow.grams_co2_per_kwh(), 20.0);
assert_eq!(GridIntensity::Low.grams_co2_per_kwh(), 50.0);
assert_eq!(GridIntensity::Custom(100.0).grams_co2_per_kwh(), 100.0);
}
#[test]
fn test_cloud_provider_pue() {
assert!(CloudProvider::Gcp.pue() < CloudProvider::Local.pue());
assert!(CloudProvider::Aws.pue() < CloudProvider::SelfHosted.pue());
}
#[test]
fn test_cloud_provider_green_option() {
assert!(CloudProvider::Gcp.has_green_option());
assert!(CloudProvider::Aws.has_green_option());
assert!(!CloudProvider::Local.has_green_option());
}
#[test]
fn test_llm_model_energy() {
assert!(LlmModel::GptLarge.wh_per_1k_tokens() > LlmModel::Tiny.wh_per_1k_tokens());
assert!(LlmModel::GptMedium.wh_per_1k_tokens() < LlmModel::GptLarge.wh_per_1k_tokens());
}
#[test]
fn test_emission_record_creation() {
let record = EmissionRecord::new(EmissionSource::LlmApiCall, 10.0)
.with_energy(5.0)
.with_description("Test call");
assert_eq!(record.source, EmissionSource::LlmApiCall);
assert_eq!(record.co2e_grams, 10.0);
assert_eq!(record.energy_wh, 5.0);
}
#[test]
fn test_optimization_creation() {
let opt = Optimization::new("Test", "Description")
.with_savings(100.0)
.with_effort(EffortLevel::High)
.with_priority(Priority::Critical);
assert_eq!(opt.title, "Test");
assert_eq!(opt.estimated_savings_grams, 100.0);
assert_eq!(opt.effort, EffortLevel::High);
assert_eq!(opt.priority, Priority::Critical);
}
#[test]
fn test_green_hosting_creation() {
let hosting = GreenHosting::new("GCP", "us-central1")
.with_renewable(100)
.carbon_neutral();
assert_eq!(hosting.provider, "GCP");
assert_eq!(hosting.renewable_percentage, 100);
assert!(hosting.carbon_neutral);
}
#[test]
fn test_emission_calculator_energy_to_co2e() {
let calc = EmissionCalculator::new()
.with_intensity(GridIntensity::Medium)
.with_provider(CloudProvider::Local);
let co2e = calc.energy_to_co2e(1000.0);
assert!((co2e - 500.0).abs() < 0.01);
}
#[test]
fn test_emission_calculator_llm_call() {
let calc = EmissionCalculator::new().with_intensity(GridIntensity::Medium);
let record = calc.llm_call_emission(LlmModel::GptLarge, 1000);
assert!(record.energy_wh > 0.0);
assert!(record.co2e_grams > 0.0);
assert_eq!(record.source, EmissionSource::LlmApiCall);
}
#[test]
fn test_emission_calculator_cpu() {
let calc = EmissionCalculator::new();
let record = calc.cpu_emission(Duration::from_secs(3600), 100.0);
assert!(record.energy_wh > 0.0);
assert!(record.co2e_grams > 0.0);
assert_eq!(record.source, EmissionSource::CpuCompute);
}
#[test]
fn test_emission_calculator_gpu() {
let calc = EmissionCalculator::new();
let record = calc.gpu_emission(Duration::from_secs(1800), 300.0);
assert!(record.energy_wh > 0.0);
assert!(record.co2e_grams > 0.0);
assert_eq!(record.source, EmissionSource::GpuCompute);
}
#[test]
fn test_emission_calculator_data_transfer() {
let calc = EmissionCalculator::new();
let record = calc.data_transfer_emission(1024 * 1024 * 1024);
assert!(record.energy_wh > 0.0);
assert!(record.co2e_grams > 0.0);
}
#[test]
fn test_carbon_tracker_creation() {
let tracker = CarbonTracker::new();
assert_eq!(tracker.record_count(), 0);
assert_eq!(tracker.total_co2e(), 0.0);
}
#[test]
fn test_carbon_tracker_track_llm() {
let mut tracker = CarbonTracker::new();
tracker.track_llm_call(LlmModel::GptLarge, 1000);
assert_eq!(tracker.record_count(), 1);
assert!(tracker.total_co2e() > 0.0);
}
#[test]
fn test_carbon_tracker_track_multiple() {
let mut tracker = CarbonTracker::new();
tracker.track_llm_call(LlmModel::GptLarge, 1000);
tracker.track_cpu(Duration::from_secs(60), 50.0);
tracker.track_data_transfer(1024 * 1024);
assert_eq!(tracker.record_count(), 3);
}
#[test]
fn test_carbon_tracker_by_source() {
let mut tracker = CarbonTracker::new();
tracker.track_llm_call(LlmModel::GptLarge, 1000);
tracker.track_cpu(Duration::from_secs(60), 50.0);
let by_source = tracker.by_source();
assert!(by_source.contains_key(&EmissionSource::LlmApiCall));
assert!(by_source.contains_key(&EmissionSource::CpuCompute));
}
#[test]
fn test_carbon_tracker_suggest_optimizations() {
let mut tracker = CarbonTracker::new();
for _ in 0..100 {
tracker.track_llm_call(LlmModel::GptLarge, 10000);
}
let suggestions = tracker.suggest_optimizations();
assert!(!suggestions.is_empty());
}
#[test]
fn test_carbon_tracker_green_hosting_recommendations() {
let tracker = CarbonTracker::new();
let recommendations = tracker.green_hosting_recommendations();
assert!(!recommendations.is_empty());
assert!(recommendations.iter().any(|r| r.carbon_neutral));
}
#[test]
fn test_carbon_report_generation() {
let mut tracker = CarbonTracker::new();
tracker.track_llm_call(LlmModel::GptMedium, 500);
let report = tracker.generate_report();
assert!(report.total_co2e_grams > 0.0);
assert_eq!(report.record_count, 1);
}
#[test]
fn test_carbon_report_markdown() {
let mut tracker = CarbonTracker::new();
tracker.track_llm_call(LlmModel::GptMedium, 500);
let report = tracker.generate_report();
let md = report.to_markdown();
assert!(md.contains("# Carbon Footprint Report"));
assert!(md.contains("Summary"));
}
#[test]
fn test_carbon_equivalents() {
let equiv = CarbonEquivalents::from_co2e_grams(120.0);
assert!((equiv.car_km - 1.0).abs() < 0.01);
assert!(equiv.smartphone_charges > 0);
}
#[test]
fn test_carbon_budget_creation() {
let budget = CarbonBudget::new(100.0);
assert_eq!(budget.daily_grams, 100.0);
assert_eq!(budget.weekly_grams, 700.0);
assert_eq!(budget.used_today, 0.0);
}
#[test]
fn test_carbon_budget_add_usage() {
let mut budget = CarbonBudget::new(100.0);
budget.add_usage(50.0);
assert_eq!(budget.used_today, 50.0);
assert_eq!(budget.daily_percentage(), 50.0);
assert!(!budget.over_daily());
}
#[test]
fn test_carbon_budget_over_daily() {
let mut budget = CarbonBudget::new(100.0);
budget.add_usage(150.0);
assert!(budget.over_daily());
assert_eq!(budget.remaining_today(), 0.0);
}
#[test]
fn test_carbon_budget_reset() {
let mut budget = CarbonBudget::new(100.0);
budget.add_usage(50.0);
budget.reset_daily();
assert_eq!(budget.used_today, 0.0);
assert_eq!(budget.used_week, 50.0); }
#[test]
fn test_unique_emission_ids() {
let r1 = EmissionRecord::new(EmissionSource::Other, 0.0);
let r2 = EmissionRecord::new(EmissionSource::Other, 0.0);
assert_ne!(r1.id, r2.id);
}
#[test]
fn test_unique_report_ids() {
let tracker = CarbonTracker::new();
let r1 = tracker.generate_report();
let r2 = tracker.generate_report();
assert_ne!(r1.id, r2.id);
}
#[test]
fn test_effort_level_ordering() {
assert!(EffortLevel::Low < EffortLevel::Medium);
assert!(EffortLevel::Medium < EffortLevel::High);
}
#[test]
fn test_priority_ordering() {
assert!(Priority::Low < Priority::Medium);
assert!(Priority::High < Priority::Critical);
}
#[test]
fn test_green_hosting_renewable_clamping() {
let hosting = GreenHosting::new("Test", "region").with_renewable(150);
assert_eq!(hosting.renewable_percentage, 100);
}
#[test]
fn test_optimization_category_display() {
assert_eq!(format!("{}", OptimizationCategory::Compute), "Compute");
assert_eq!(
format!("{}", OptimizationCategory::ModelSelection),
"Model Selection"
);
}
#[test]
fn test_cloud_provider_display() {
assert_eq!(format!("{}", CloudProvider::Aws), "AWS");
assert_eq!(format!("{}", CloudProvider::Gcp), "GCP");
}
#[test]
fn test_llm_model_display() {
assert_eq!(format!("{}", LlmModel::GptLarge), "GPT-4/Large");
assert_eq!(format!("{}", LlmModel::Claude), "Claude");
}
#[test]
fn test_emission_record_with_all_fields() {
let record = EmissionRecord::new(EmissionSource::Build, 50.0)
.with_energy(100.0)
.with_duration(Duration::from_secs(300))
.with_description("Build project")
.with_operation("cargo build")
.with_provider(CloudProvider::Local)
.with_region("local");
assert!(record.duration.is_some());
assert!(record.operation.is_some());
assert!(record.provider.is_some());
assert!(record.region.is_some());
}
#[test]
fn test_carbon_tracker_with_config() {
let tracker = CarbonTracker::new()
.with_provider(CloudProvider::Gcp)
.with_region("us-central1");
assert_eq!(tracker.provider, CloudProvider::Gcp);
assert_eq!(tracker.region, Some("us-central1".to_string()));
}
#[test]
fn test_build_emission() {
let calc = EmissionCalculator::new();
let record = calc.build_emission(Duration::from_secs(60), 4, 15.0);
assert!(record.energy_wh > 0.0);
assert!(record.co2e_grams > 0.0);
assert_eq!(record.source, EmissionSource::Build);
}
#[test]
fn test_storage_emission() {
let calc = EmissionCalculator::new();
let record = calc.storage_emission(10.0);
assert!(record.energy_wh > 0.0);
assert!(record.co2e_grams > 0.0);
assert_eq!(record.source, EmissionSource::Storage);
}
}