sim_lib_view_device/
degrade.rs1use crate::ladder::DeviceTier;
4use crate::profile::{DeviceProfile, DeviceProfileParts, derive_tier, has_symbol, push_existing};
5use sim_kernel::{Expr, Symbol};
6
7#[derive(Clone, Debug, Default, PartialEq, Eq)]
9pub struct ObservedRoute {
10 pub display: Vec<Symbol>,
12 pub input: Vec<Symbol>,
14 pub output: Vec<Symbol>,
16 pub links: Vec<Symbol>,
18 pub streams: Vec<Symbol>,
20}
21
22impl ObservedRoute {
23 pub fn from_profile(profile: &DeviceProfile) -> Self {
25 Self {
26 display: profile.display.clone(),
27 input: profile.input.clone(),
28 output: profile.output.clone(),
29 links: profile.links.clone(),
30 streams: profile.streams.clone(),
31 }
32 }
33}
34
35#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct Degradation {
38 pub tier: DeviceTier,
40 pub reasons: Vec<String>,
42}
43
44#[derive(Clone, Debug, Default)]
46pub struct DegradationResolver;
47
48impl DegradationResolver {
49 pub fn resolve(requested: &DeviceProfile, observed: &ObservedRoute) -> Degradation {
52 let supported = DeviceProfile::new(DeviceProfileParts {
53 kind: requested.kind.clone(),
54 display: intersection(&requested.display, &observed.display),
55 input: intersection(&requested.input, &observed.input),
56 output: intersection(&requested.output, &observed.output),
57 links: intersection(&requested.links, &observed.links),
58 streams: intersection(&requested.streams, &observed.streams),
59 rate: requested.rate,
60 policy: Expr::Map(Vec::new()),
61 });
62 let mut reasons = Vec::new();
63 missing_reasons(
64 &mut reasons,
65 "display",
66 &requested.display,
67 &observed.display,
68 );
69 missing_reasons(&mut reasons, "input", &requested.input, &observed.input);
70 missing_reasons(&mut reasons, "output", &requested.output, &observed.output);
71 missing_reasons(&mut reasons, "link", &requested.links, &observed.links);
72 missing_reasons(
73 &mut reasons,
74 "stream",
75 &requested.streams,
76 &observed.streams,
77 );
78 Degradation {
79 tier: derive_tier(&supported),
80 reasons,
81 }
82 }
83
84 pub fn unavailable(reason: impl Into<String>) -> Degradation {
86 Degradation {
87 tier: DeviceTier::Display,
88 reasons: vec![reason.into()],
89 }
90 }
91}
92
93fn intersection(requested: &[Symbol], observed: &[Symbol]) -> Vec<Symbol> {
94 let mut out = Vec::new();
95 for symbol in requested {
96 if observed.iter().any(|candidate| candidate == symbol) {
97 push_existing(&mut out, symbol.clone());
98 }
99 }
100 out
101}
102
103fn missing_reasons(
104 reasons: &mut Vec<String>,
105 lane: &str,
106 requested: &[Symbol],
107 observed: &[Symbol],
108) {
109 for symbol in requested {
110 if !has_symbol(observed, symbol.name.as_ref()) {
111 reasons.push(format!("missing {lane}: {}", symbol.as_qualified_str()));
112 }
113 }
114}