#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum AggregationRule {
Sum {
source_malo_ids: Vec<String>,
},
Residual {
total_malo_id: String,
subtract_malo_ids: Vec<String>,
},
PvSelfConsumption {
grid_malo_id: String,
generation_malo_id: String,
},
GgvConstantAllocation {
plant_melo_id: String,
tenant_melo_id: String,
fraction: rust_decimal::Decimal,
},
GgvProportionalAllocation {
plant_melo_id: String,
tenant_melo_id: String,
all_tenant_melo_ids: Vec<String>,
},
}
impl AggregationRule {
#[must_use]
pub fn source_malo_ids(&self) -> Vec<&str> {
match self {
Self::Sum { source_malo_ids } => source_malo_ids.iter().map(String::as_str).collect(),
Self::Residual {
total_malo_id,
subtract_malo_ids,
} => {
let mut ids = vec![total_malo_id.as_str()];
ids.extend(subtract_malo_ids.iter().map(String::as_str));
ids
}
Self::PvSelfConsumption {
grid_malo_id,
generation_malo_id,
} => vec![grid_malo_id.as_str(), generation_malo_id.as_str()],
Self::GgvConstantAllocation {
plant_melo_id,
tenant_melo_id,
..
} => vec![plant_melo_id.as_str(), tenant_melo_id.as_str()],
Self::GgvProportionalAllocation {
plant_melo_id,
all_tenant_melo_ids,
..
} => {
let mut ids = vec![plant_melo_id.as_str()];
ids.extend(all_tenant_melo_ids.iter().map(String::as_str));
ids
}
}
}
#[must_use]
pub fn rule_type(&self) -> &'static str {
match self {
Self::Sum { .. } => "Sum",
Self::Residual { .. } => "Residual",
Self::PvSelfConsumption { .. } => "PvSelfConsumption",
Self::GgvConstantAllocation { .. } => "GgvConstantAllocation",
Self::GgvProportionalAllocation { .. } => "GgvProportionalAllocation",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
#[test]
fn sum_rule_lists_all_sources() {
let rule = AggregationRule::Sum {
source_malo_ids: vec!["MALO_A".to_owned(), "MALO_B".to_owned()],
};
let sources = rule.source_malo_ids();
assert_eq!(sources, vec!["MALO_A", "MALO_B"]);
assert_eq!(rule.rule_type(), "Sum");
}
#[test]
fn residual_rule_includes_total_and_subtracts() {
let rule = AggregationRule::Residual {
total_malo_id: "TOTAL".to_owned(),
subtract_malo_ids: vec!["PV".to_owned()],
};
let sources = rule.source_malo_ids();
assert!(sources.contains(&"TOTAL"));
assert!(sources.contains(&"PV"));
assert_eq!(rule.rule_type(), "Residual");
}
#[test]
fn ggv_constant_lists_plant_and_tenant() {
let rule = AggregationRule::GgvConstantAllocation {
plant_melo_id: "MELO_PLANT".to_owned(),
tenant_melo_id: "MELO_T1".to_owned(),
fraction: dec!(0.10),
};
let sources = rule.source_malo_ids();
assert_eq!(sources.len(), 2);
assert!(sources.contains(&"MELO_PLANT"));
assert!(sources.contains(&"MELO_T1"));
assert_eq!(rule.rule_type(), "GgvConstantAllocation");
}
#[test]
fn ggv_proportional_lists_plant_and_all_tenants() {
let rule = AggregationRule::GgvProportionalAllocation {
plant_melo_id: "MELO_PLANT".to_owned(),
tenant_melo_id: "MELO_T1".to_owned(),
all_tenant_melo_ids: vec!["MELO_T1".to_owned(), "MELO_T2".to_owned()],
};
let sources = rule.source_malo_ids();
assert_eq!(sources.len(), 3);
assert!(sources.contains(&"MELO_PLANT"));
assert_eq!(rule.rule_type(), "GgvProportionalAllocation");
}
}