artifactize_core/
query.rs1mod summary;
4pub use summary::{
5 Kinds, Source, SourceKind, profile_name, request_output, request_summary, reused, run_output,
6 run_summary, source,
7};
8
9use std::{collections::BTreeMap, path::Path};
10
11use serde::Serialize;
12
13use crate::{
14 config::{Artifact, Eval, RepoConfig},
15 graph::Graph,
16 project::selection::Selection,
17 scope::Relation,
18};
19
20const GRAPH_FORMAT_VERSION: u32 = 1;
24
25#[derive(Debug, Serialize)]
26#[serde(rename_all = "camelCase")]
27pub struct GraphView<'a> {
28 pub version: u32,
29 #[serde(serialize_with = "crate::platform::path_serde::serialize")]
30 pub repo_path: &'a Path,
31 pub selection: &'a Selection,
32 pub artifacts: BTreeMap<&'a crate::config::ArtifactName, &'a Artifact>,
33 pub evals: Vec<&'a Eval>,
34 pub relations: Vec<GraphRelation<'a>>,
35 pub components: Vec<GraphComponent<'a>>,
36}
37
38#[derive(Debug, Serialize)]
39pub struct GraphRelation<'a> {
40 #[serde(flatten)]
41 pub relation: &'a Relation,
42 pub cyclic: bool,
43}
44
45#[derive(Debug, Serialize)]
46pub struct GraphComponent<'a> {
47 pub id: crate::graph::ComponentId,
48 pub artifacts: Vec<&'a crate::config::ArtifactName>,
49 pub dependencies: Vec<crate::graph::ComponentId>,
50 pub gates: Vec<&'a crate::config::EvalId>,
51 pub cyclic: bool,
52}
53
54pub fn graph<'a>(
56 config: &'a RepoConfig,
57 selection: &'a Selection,
58) -> Result<GraphView<'a>, String> {
59 let graph = Graph::new(config).map_err(|error| error.to_string())?;
60 let selected = selection.resolve(config)?;
61 let required = graph
62 .dependency_closure(&selected.roots)
63 .map_err(|error| error.to_string())?;
64 let artifacts: BTreeMap<_, _> = required
65 .iter()
66 .map(|&id| (id, &config.artifacts[id]))
67 .collect();
68 let evals = config
69 .evals
70 .iter()
71 .filter(|eval| artifacts.contains_key(&eval.target))
72 .collect();
73 let relations: Vec<_> = config
74 .relations
75 .iter()
76 .filter(|relation| {
77 artifacts.contains_key(&relation.source) && artifacts.contains_key(&relation.target)
78 })
79 .map(|relation| GraphRelation {
80 relation,
81 cyclic: graph.artifacts()[relation.source.as_str()].component
82 == graph.artifacts()[relation.target.as_str()].component,
83 })
84 .collect();
85 let components = graph
86 .components()
87 .iter()
88 .enumerate()
89 .filter(|(_, component)| artifacts.contains_key(component.artifacts[0]))
90 .map(|(id, component)| GraphComponent {
91 id: crate::graph::ComponentId::from_position(id),
92 artifacts: component.artifacts.clone(),
93 dependencies: component.dependencies.clone(),
94 gates: component.gates.clone(),
95 cyclic: component.artifacts.len() > 1
96 || relations.iter().any(|edge| {
97 edge.cyclic && component.artifacts.contains(&&edge.relation.source)
98 }),
99 })
100 .collect();
101 Ok(GraphView {
102 version: GRAPH_FORMAT_VERSION,
103 repo_path: &config.root,
104 selection,
105 artifacts,
106 evals,
107 relations,
108 components,
109 })
110}
111
112pub fn impact(config: &RepoConfig, selection: &Selection) -> Result<Selection, String> {
115 let resolved = selection.resolve(config)?;
116 let mut affected: std::collections::BTreeSet<_> = resolved.roots.into_iter().cloned().collect();
117 loop {
118 let consumers: Vec<_> = config
119 .relations
120 .iter()
121 .filter(|edge| affected.contains(&edge.source))
122 .map(|edge| edge.target.clone())
123 .collect();
124 let before = affected.len();
125 affected.extend(consumers);
126 if affected.len() == before {
127 break;
128 }
129 }
130 Ok(Selection::Artifacts {
131 artifact_ids: affected.into_iter().collect(),
132 })
133}