use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::surface_graph::{ApplicationSurfaceGraph, ApplicationSurfaceKind};
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MeasuredColumn {
pub present: BTreeSet<String>,
pub absent: BTreeSet<String>,
}
impl MeasuredColumn {
#[must_use]
pub fn closed_world(graph: &ApplicationSurfaceGraph, present: BTreeSet<String>) -> Self {
let absent = graph
.surfaces
.iter()
.map(|surface| surface.id.clone())
.filter(|id| !present.contains(id))
.collect();
Self { present, absent }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct SurfaceEvidenceColumns {
pub intent: Option<MeasuredColumn>,
pub runtime: Option<MeasuredColumn>,
pub test: Option<MeasuredColumn>,
pub proof: Option<MeasuredColumn>,
pub coverage: Option<MeasuredColumn>,
pub protection: Option<MeasuredColumn>,
pub ui: Option<MeasuredColumn>,
pub a11y: Option<MeasuredColumn>,
pub mutation: Option<MeasuredColumn>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EvidenceCell {
Present,
Absent,
Unmeasured,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SurfaceEvidenceRow {
pub surface: String,
pub kind: ApplicationSurfaceKind,
pub intent: EvidenceCell,
pub runtime: EvidenceCell,
pub test: EvidenceCell,
pub proof: EvidenceCell,
pub coverage: EvidenceCell,
pub protection: EvidenceCell,
pub ui: EvidenceCell,
pub a11y: EvidenceCell,
pub mutation: EvidenceCell,
}
impl SurfaceEvidenceRow {
#[must_use]
pub fn cells(&self) -> [(crate::evidence_plan::EvidenceColumn, EvidenceCell); 9] {
use crate::evidence_plan::EvidenceColumn as Column;
[
(Column::Intent, self.intent),
(Column::Runtime, self.runtime),
(Column::Test, self.test),
(Column::Proof, self.proof),
(Column::Coverage, self.coverage),
(Column::Protection, self.protection),
(Column::Ui, self.ui),
(Column::A11y, self.a11y),
(Column::Mutation, self.mutation),
]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SurfaceEvidenceMatrix {
pub surfaces: Vec<SurfaceEvidenceRow>,
pub truncated: bool,
}
#[must_use]
pub fn surface_evidence_matrix(
graph: &ApplicationSurfaceGraph,
columns: &SurfaceEvidenceColumns,
) -> SurfaceEvidenceMatrix {
let mut surfaces = graph
.surfaces
.iter()
.map(|surface| SurfaceEvidenceRow {
surface: surface.id.clone(),
kind: surface.kind,
intent: cell(columns.intent.as_ref(), &surface.id),
runtime: cell(columns.runtime.as_ref(), &surface.id),
test: cell(columns.test.as_ref(), &surface.id),
proof: cell(columns.proof.as_ref(), &surface.id),
coverage: cell(columns.coverage.as_ref(), &surface.id),
protection: cell(columns.protection.as_ref(), &surface.id),
ui: cell(columns.ui.as_ref(), &surface.id),
a11y: cell(columns.a11y.as_ref(), &surface.id),
mutation: cell(columns.mutation.as_ref(), &surface.id),
})
.collect::<Vec<_>>();
surfaces.sort_by(|left, right| left.surface.cmp(&right.surface));
SurfaceEvidenceMatrix {
surfaces,
truncated: graph.truncated,
}
}
#[must_use]
pub fn surfaces_touching_nodes<'a, I, S>(graph: &ApplicationSurfaceGraph, nodes: I) -> BTreeSet<String>
where
I: IntoIterator<Item = &'a S>,
S: AsRef<str> + 'a,
{
let wanted = nodes
.into_iter()
.map(|node| node.as_ref().to_owned())
.collect::<BTreeSet<_>>();
if wanted.is_empty() {
return BTreeSet::new();
}
graph
.surfaces
.iter()
.filter(|surface| {
surface
.implementation_nodes
.iter()
.any(|node| wanted.contains(node))
})
.map(|surface| surface.id.clone())
.collect()
}
fn cell(column: Option<&MeasuredColumn>, surface: &str) -> EvidenceCell {
let Some(column) = column else {
return EvidenceCell::Unmeasured;
};
match (
column.present.contains(surface),
column.absent.contains(surface),
) {
(true, false) => EvidenceCell::Present,
(false, true) => EvidenceCell::Absent,
_ => EvidenceCell::Unmeasured,
}
}