ephemeris_release/adapters/cargo/
version_groups.rs1use std::collections::{BTreeMap, BTreeSet};
4
5use super::workspace::{VersionSource, WorkspaceError};
6use crate::version::Version;
7
8#[derive(Clone, Debug, Default)]
12pub(crate) struct VersionGroups(BTreeMap<VersionSource, Vec<String>>);
13
14impl VersionGroups {
15 pub(crate) fn new(packages: impl Iterator<Item = (VersionSource, String)>) -> Self {
16 let mut groups = BTreeMap::<VersionSource, Vec<String>>::new();
17 for (source, name) in packages {
18 groups.entry(source).or_default().push(name);
19 }
20 for members in groups.values_mut() {
21 members.sort();
22 members.dedup();
23 }
24 Self(groups)
25 }
26
27 pub(crate) fn iter(&self) -> impl Iterator<Item = (&VersionSource, &[String])> {
28 self.0
29 .iter()
30 .map(|(source, members)| (source, members.as_slice()))
31 }
32
33 pub(crate) fn validate_selection(
35 &self,
36 selected: &BTreeSet<String>,
37 ) -> Result<(), WorkspaceError> {
38 for (source, members) in self.iter() {
39 if members.iter().any(|name| selected.contains(name)) {
40 let missing: Vec<_> = members
41 .iter()
42 .filter(|name| !selected.contains(*name))
43 .cloned()
44 .collect();
45 if !missing.is_empty() {
46 return Err(WorkspaceError::IncompleteVersionGroup {
47 version_source: source.clone(),
48 missing,
49 });
50 }
51 }
52 }
53 Ok(())
54 }
55
56 pub(crate) fn complete_explicit(
59 &self,
60 proposals: &BTreeMap<String, Version>,
61 ) -> Result<BTreeMap<String, Version>, WorkspaceError> {
62 let mut completed = proposals.clone();
63 for (source, members) in self.iter() {
64 let targets = distinct(members.iter().filter_map(|name| proposals.get(name)));
65 let target = match targets.as_slice() {
66 [] => continue,
67 [target] => (*target).clone(),
68 [_, _, ..] => {
69 return Err(WorkspaceError::ConflictingVersionGroup {
70 version_source: source.clone(),
71 members: members.to_vec(),
72 });
73 }
74 };
75 completed.extend(
76 members
77 .iter()
78 .map(|member| (member.clone(), target.clone())),
79 );
80 }
81 Ok(completed)
82 }
83
84 pub(crate) fn complete_automatic(
86 &self,
87 proposals: &BTreeMap<String, Version>,
88 ) -> BTreeMap<String, Version> {
89 let mut completed = proposals.clone();
90 for (_, members) in self.iter() {
91 let Some(target) = members
92 .iter()
93 .filter_map(|name| proposals.get(name))
94 .max_by(|left, right| left.precedence_cmp(right))
95 else {
96 continue;
97 };
98 for member in members {
99 completed.insert(member.clone(), target.clone());
100 }
101 }
102 completed
103 }
104}
105
106pub(crate) fn distinct<'a, V: PartialEq>(values: impl Iterator<Item = &'a V>) -> Vec<&'a V> {
108 let mut distinct = Vec::new();
109 for value in values {
110 if !distinct.contains(&value) {
111 distinct.push(value);
112 }
113 }
114 distinct
115}