mod emit;
mod predicates;
mod resolve;
#[cfg(test)]
mod tests;
use std::collections::BTreeSet;
use crate::{ComponentGraph, Edge, Error, Node};
use super::expr::Expr;
use emit::{diamond_term, measure, subtraction_term, sum};
pub(super) use predicates::ids_with_telemetry;
pub(crate) use predicates::is_grid_meter;
use resolve::{Measurement, measurement_points};
#[derive(Clone, Copy)]
pub(crate) enum SourcePreference {
ComponentsFirst,
MetersFirst,
MetersFirstWithChains,
}
impl SourcePreference {
pub(crate) fn prefer_meters(prefer_meters: bool) -> Self {
if prefer_meters {
SourcePreference::MetersFirst
} else {
SourcePreference::ComponentsFirst
}
}
fn meters_first(self) -> bool {
matches!(
self,
SourcePreference::MetersFirst | SourcePreference::MetersFirstWithChains
)
}
fn allows_meter_chains(self) -> bool {
matches!(self, SourcePreference::MetersFirstWithChains)
}
}
pub(crate) fn aggregate<N: Node, E: Edge>(
graph: &ComponentGraph<N, E>,
targets: BTreeSet<u64>,
policy: SourcePreference,
) -> Result<Expr, Error> {
sum(aggregate_terms(graph, targets, policy)?)
.ok_or(Error::internal("No components to generate formula."))
}
pub(crate) fn aggregate_terms<N: Node, E: Edge>(
graph: &ComponentGraph<N, E>,
targets: BTreeSet<u64>,
policy: SourcePreference,
) -> Result<Vec<Expr>, Error> {
if graph.config.disable_fallback_components {
let mut terms: Vec<Expr> = ids_with_telemetry(graph, targets.iter().copied())?
.into_iter()
.map(Expr::component)
.collect();
if terms.is_empty() && !targets.is_empty() {
terms.push(Expr::number(0.0));
}
Ok(terms)
} else {
measurement_points(graph, &targets)?
.into_iter()
.map(|point| match point {
Measurement::Single(id) => measure(graph, id, policy),
Measurement::Diamond { components, meters } => {
diamond_term(graph, &components, &meters, policy)
}
Measurement::Subtraction {
parent_meters,
subtracted,
components,
} => subtraction_term(graph, &parent_meters, &subtracted, &components, policy),
})
.collect()
}
}