mod summary;
pub use summary::{
Kinds, Source, SourceKind, profile_name, request_output, request_summary, reused, run_output,
run_summary, source,
};
use std::{collections::BTreeMap, path::Path};
use serde::Serialize;
use crate::{
config::{Artifact, Eval, RepoConfig},
graph::Graph,
project::selection::Selection,
scope::Relation,
};
const GRAPH_FORMAT_VERSION: u32 = 1;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GraphView<'a> {
pub version: u32,
#[serde(serialize_with = "crate::platform::path_serde::serialize")]
pub repo_path: &'a Path,
pub selection: &'a Selection,
pub artifacts: BTreeMap<&'a crate::config::ArtifactName, &'a Artifact>,
pub evals: Vec<&'a Eval>,
pub relations: Vec<GraphRelation<'a>>,
pub components: Vec<GraphComponent<'a>>,
}
#[derive(Debug, Serialize)]
pub struct GraphRelation<'a> {
#[serde(flatten)]
pub relation: &'a Relation,
pub cyclic: bool,
}
#[derive(Debug, Serialize)]
pub struct GraphComponent<'a> {
pub id: crate::graph::ComponentId,
pub artifacts: Vec<&'a crate::config::ArtifactName>,
pub dependencies: Vec<crate::graph::ComponentId>,
pub gates: Vec<&'a crate::config::EvalId>,
pub cyclic: bool,
}
pub fn graph<'a>(
config: &'a RepoConfig,
selection: &'a Selection,
) -> Result<GraphView<'a>, String> {
let graph = Graph::new(config).map_err(|error| error.to_string())?;
let selected = selection.resolve(config)?;
let required = graph
.dependency_closure(&selected.roots)
.map_err(|error| error.to_string())?;
let artifacts: BTreeMap<_, _> = required
.iter()
.map(|&id| (id, &config.artifacts[id]))
.collect();
let evals = config
.evals
.iter()
.filter(|eval| artifacts.contains_key(&eval.target))
.collect();
let relations: Vec<_> = config
.relations
.iter()
.filter(|relation| {
artifacts.contains_key(&relation.source) && artifacts.contains_key(&relation.target)
})
.map(|relation| GraphRelation {
relation,
cyclic: graph.artifacts()[relation.source.as_str()].component
== graph.artifacts()[relation.target.as_str()].component,
})
.collect();
let components = graph
.components()
.iter()
.enumerate()
.filter(|(_, component)| artifacts.contains_key(component.artifacts[0]))
.map(|(id, component)| GraphComponent {
id: crate::graph::ComponentId::from_position(id),
artifacts: component.artifacts.clone(),
dependencies: component.dependencies.clone(),
gates: component.gates.clone(),
cyclic: component.artifacts.len() > 1
|| relations.iter().any(|edge| {
edge.cyclic && component.artifacts.contains(&&edge.relation.source)
}),
})
.collect();
Ok(GraphView {
version: GRAPH_FORMAT_VERSION,
repo_path: &config.root,
selection,
artifacts,
evals,
relations,
components,
})
}
pub fn impact(config: &RepoConfig, selection: &Selection) -> Result<Selection, String> {
let resolved = selection.resolve(config)?;
let mut affected: std::collections::BTreeSet<_> = resolved.roots.into_iter().cloned().collect();
loop {
let consumers: Vec<_> = config
.relations
.iter()
.filter(|edge| affected.contains(&edge.source))
.map(|edge| edge.target.clone())
.collect();
let before = affected.len();
affected.extend(consumers);
if affected.len() == before {
break;
}
}
Ok(Selection::Artifacts {
artifact_ids: affected.into_iter().collect(),
})
}