Skip to main content

callisto_graph/
groups.rs

1use std::collections::BTreeMap;
2
3use crate::config::groups::{GroupDef, GroupMember, GroupMemberKind, GroupTable};
4use crate::error::GraphError;
5use crate::napi::NapiTargetsIndex;
6use crate::resolver::DependencyResolver;
7use crate::tags::TagIndex;
8use callisto_format::Versioning;
9use callisto_model::{Diagnostic, GroupName, PackageId, Severity, Version};
10
11#[derive(Clone, Debug, Default)]
12pub struct GroupCheckOutcome {
13    pub new_members: BTreeMap<GroupName, Vec<PackageId>>,
14    pub diagnostics: Vec<Diagnostic>,
15}
16
17pub fn pre_mutation_checks<D: DependencyResolver>(
18    _graph: &D,
19    groups: &GroupTable,
20    base: &BTreeMap<PackageId, Version>,
21    tags: &TagIndex,
22    _napi: &NapiTargetsIndex,
23) -> Result<GroupCheckOutcome, GraphError> {
24    let mut outcome = GroupCheckOutcome::default();
25
26    for g in groups.fixed.values() {
27        let released: Vec<PackageId> = g
28            .members(GroupMemberKind::Package)
29            .filter_map(|m| match m {
30                GroupMember::Package(ref id) => {
31                    if tags.last_tag(id).is_some() {
32                        Some(id.clone())
33                    } else {
34                        None
35                    }
36                }
37                _ => None,
38            })
39            .collect();
40
41        let fresh: Vec<PackageId> = g
42            .members(GroupMemberKind::Package)
43            .filter_map(|m| match m {
44                GroupMember::Package(ref id) => {
45                    if tags.last_tag(id).is_none() {
46                        Some(id.clone())
47                    } else {
48                        None
49                    }
50                }
51                _ => None,
52            })
53            .collect();
54
55        let pairs: Vec<(PackageId, Version)> = released
56            .iter()
57            .filter_map(|id| base.get(id).map(|v| (id.clone(), v.clone())))
58            .collect();
59
60        if pairs.len() > 1 {
61            let first_v = &pairs[0].1;
62            let mut divergent = false;
63            for (_id, v) in &pairs[1..] {
64                if v.grammar() != first_v.grammar() {
65                    return Err(GraphError::GroupGrammarMismatch {
66                        group: g.name.clone(),
67                        members: pairs,
68                    });
69                }
70                if Version::compare(v, first_v).ok() != Some(std::cmp::Ordering::Equal) {
71                    divergent = true;
72                }
73            }
74            if divergent {
75                return Err(GraphError::FixedGroupDivergent {
76                    group: g.name.clone(),
77                    members: pairs,
78                });
79            }
80        }
81
82        if !fresh.is_empty() {
83            outcome.new_members.insert(g.name.clone(), fresh);
84        }
85    }
86
87    Ok(outcome)
88}
89
90pub fn fixed_group_target(
91    g: &GroupDef,
92    base: &BTreeMap<PackageId, Version>,
93    severities: &BTreeMap<PackageId, Severity>,
94    tags: &TagIndex,
95    _pre: Option<&callisto_format::PreState>,
96) -> Result<Version, GraphError> {
97    let released: Vec<PackageId> = g
98        .members(GroupMemberKind::Package)
99        .filter_map(|m| match m {
100            GroupMember::Package(ref id) => {
101                if tags.last_tag(id).is_some() {
102                    Some(id.clone())
103                } else {
104                    None
105                }
106            }
107            _ => None,
108        })
109        .collect();
110
111    let mut max_sev = Severity::None;
112    for m in g.members(GroupMemberKind::Package) {
113        if let GroupMember::Package(ref id) = m {
114            if let Some(&s) = severities.get(id) {
115                if s > max_sev {
116                    max_sev = s;
117                }
118            }
119        }
120    }
121
122    let aligned_base = if !released.is_empty() {
123        base.get(&released[0])
124            .cloned()
125            .unwrap_or_else(|| Version::semver(1, 0, 0))
126    } else {
127        Version::semver(0, 0, 0)
128    };
129
130    let versioning = callisto_format::SemVerVersioning;
131
132    versioning
133        .bump(&aligned_base, max_sev)
134        .map_err(GraphError::Bump)
135}