use crate::ladder::DeviceTier;
use crate::profile::{DeviceProfile, DeviceProfileParts, derive_tier, has_symbol, push_existing};
use sim_kernel::{Expr, Symbol};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ObservedRoute {
pub display: Vec<Symbol>,
pub input: Vec<Symbol>,
pub output: Vec<Symbol>,
pub links: Vec<Symbol>,
pub streams: Vec<Symbol>,
}
impl ObservedRoute {
pub fn from_profile(profile: &DeviceProfile) -> Self {
Self {
display: profile.display.clone(),
input: profile.input.clone(),
output: profile.output.clone(),
links: profile.links.clone(),
streams: profile.streams.clone(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Degradation {
pub tier: DeviceTier,
pub reasons: Vec<String>,
}
#[derive(Clone, Debug, Default)]
pub struct DegradationResolver;
impl DegradationResolver {
pub fn resolve(requested: &DeviceProfile, observed: &ObservedRoute) -> Degradation {
let supported = DeviceProfile::new(DeviceProfileParts {
kind: requested.kind.clone(),
display: intersection(&requested.display, &observed.display),
input: intersection(&requested.input, &observed.input),
output: intersection(&requested.output, &observed.output),
links: intersection(&requested.links, &observed.links),
streams: intersection(&requested.streams, &observed.streams),
rate: requested.rate,
policy: Expr::Map(Vec::new()),
});
let mut reasons = Vec::new();
missing_reasons(
&mut reasons,
"display",
&requested.display,
&observed.display,
);
missing_reasons(&mut reasons, "input", &requested.input, &observed.input);
missing_reasons(&mut reasons, "output", &requested.output, &observed.output);
missing_reasons(&mut reasons, "link", &requested.links, &observed.links);
missing_reasons(
&mut reasons,
"stream",
&requested.streams,
&observed.streams,
);
Degradation {
tier: derive_tier(&supported),
reasons,
}
}
pub fn unavailable(reason: impl Into<String>) -> Degradation {
Degradation {
tier: DeviceTier::Display,
reasons: vec![reason.into()],
}
}
}
fn intersection(requested: &[Symbol], observed: &[Symbol]) -> Vec<Symbol> {
let mut out = Vec::new();
for symbol in requested {
if observed.iter().any(|candidate| candidate == symbol) {
push_existing(&mut out, symbol.clone());
}
}
out
}
fn missing_reasons(
reasons: &mut Vec<String>,
lane: &str,
requested: &[Symbol],
observed: &[Symbol],
) {
for symbol in requested {
if !has_symbol(observed, symbol.name.as_ref()) {
reasons.push(format!("missing {lane}: {}", symbol.as_qualified_str()));
}
}
}