heron_rebuild_workflow/branch/
spec.rs1use util::{Bitmask, IdVec};
2
3use crate::{BranchpointId, IdentId, NULL_IDENT};
4
5use super::Error;
6
7#[derive(Default, Clone, Hash, PartialEq, Eq)]
14pub struct BranchSpec {
15 branches: IdVec<BranchpointId, IdentId>,
16}
17
18impl std::fmt::Debug for BranchSpec {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
20 let mut first = true;
21 for (id, ident) in self.branches.iter().enumerate() {
22 if first {
23 first = false;
24 } else {
25 write!(f, "+")?;
26 }
27 write!(f, "{}.{}", id, usize::from(*ident))?;
28 }
29 Ok(())
30 }
31}
32
33impl BranchSpec {
34 pub fn simple(k: BranchpointId, v: IdentId) -> Self {
36 let mut branches = IdVec::with_capacity(usize::from(k) + 1);
37 branches.insert(k, v);
38 Self { branches }
39 }
40
41 #[inline]
43 pub fn insert(&mut self, k: BranchpointId, v: IdentId) {
44 self.branches.insert(k, v);
45 }
46
47 pub fn get_specified(&self, k: BranchpointId) -> Option<IdentId> {
49 if usize::from(k) < self.branches.len() {
50 none_if_baseline(
51 *self.branches.get(k).expect("Should never fail -- already checked bounds"),
52 )
53 } else {
54 None
55 }
56 }
57
58 pub fn is_unspecified(&self, k: BranchpointId) -> bool {
60 usize::from(k) >= self.branches.len()
61 || *self.branches.get(k).expect("Should never fail -- already checked bounds")
62 == NULL_IDENT
63 }
64
65 pub fn is_specified(&self, k: BranchpointId) -> bool {
67 usize::from(k) < self.branches.len()
68 && *self.branches.get(k).expect("Should never fail -- already checked bounds")
69 != NULL_IDENT
70 }
71
72 pub fn unset(&mut self, k: BranchpointId) {
74 if usize::from(k) < self.branches.len() {
75 self.branches.insert(k, NULL_IDENT)
76 }
77 }
78
79 pub fn iter(&self) -> impl Iterator<Item = &IdentId> {
81 self.branches.iter()
82 }
83
84 pub fn len(&self) -> usize {
86 self.branches.len()
87 }
88
89 pub fn is_empty(&self) -> bool {
91 self.branches.is_empty()
92 }
93
94 pub fn is_compatible(&self, other: &Self) -> bool {
97 for (k, v) in self.iter_specified() {
98 if let Some(v) = v {
99 if let Some(other_v) = other.get_specified(k.into()) {
100 if other_v != v {
101 return false;
102 }
103 }
104 }
105 }
106 true
107 }
108
109 pub fn is_exact_match(&self, other: &Self) -> bool {
111 for (k, v) in self.iter_specified() {
112 if let Some(v) = v {
113 let other_v = other.get_specified(k.into());
114 if other_v.is_none() || other_v.unwrap() != v {
115 return false;
116 }
117 }
118 }
119 true
120 }
121
122 pub fn insert_all(&mut self, other: &Self) {
124 for (k, v) in other.branches.iter().enumerate() {
125 if *v != NULL_IDENT {
126 self.branches.insert(k.into(), *v);
127 }
128 }
129 }
130
131 #[inline]
132 fn iter_specified(&self) -> impl Iterator<Item = (usize, Option<IdentId>)> + '_ {
133 self.branches.iter().cloned().map(none_if_baseline).enumerate()
134 }
135}
136
137impl BranchSpec {
139 pub fn as_mask<T>(&self) -> Result<T, Error>
140 where
141 T: Bitmask + Default,
142 {
143 if self.len() > T::BITS {
144 return Err(Error::BranchOutOfBounds(T::BITS, self.clone()));
145 }
146 let mut mask = T::default();
147 for i in 0..T::BITS {
148 if i >= self.len() {
149 break;
150 }
151 if self.is_specified(i.into()) {
152 mask.set(i);
153 }
154 }
155 Ok(mask)
156 }
157}
158
159#[inline]
160fn none_if_baseline(id: IdentId) -> Option<IdentId> {
161 if id == NULL_IDENT {
162 None
163 } else {
164 Some(id)
165 }
166}