1use crate::{Stage, StageId};
2use std::{
3collections::HashMap,
4 fmt::{Debug, Formatter},
5};
67/// Combines multiple [`Stage`]s into a single unit.
8///
9/// A [`StageSet`] is a logical chunk of stages that depend on each other. It is up to the
10/// individual stage sets to determine what kind of configuration they expose.
11///
12/// Individual stages in the set can be added, removed and overridden using [`StageSetBuilder`].
13pub trait StageSet<Provider>: Sized {
14/// Configures the stages in the set.
15fn builder(self) -> StageSetBuilder<Provider>;
1617/// Overrides the given [`Stage`], if it is in this set.
18 ///
19 /// # Panics
20 ///
21 /// Panics if the [`Stage`] is not in this set.
22fn set<S: Stage<Provider> + 'static>(self, stage: S) -> StageSetBuilder<Provider> {
23self.builder().set(stage)
24 }
25}
2627struct StageEntry<Provider> {
28 stage: Box<dyn Stage<Provider>>,
29 enabled: bool,
30}
3132impl<Provider> Debugfor StageEntry<Provider> {
33fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34f.debug_struct("StageEntry")
35 .field("stage", &self.stage.id())
36 .field("enabled", &self.enabled)
37 .finish()
38 }
39}
4041/// Helper to create and configure a [`StageSet`].
42///
43/// The builder provides ordering helpers to ensure that stages that depend on each other are added
44/// to the final sync pipeline before/after their dependencies.
45///
46/// Stages inside the set can be disabled, enabled, overridden and reordered.
47pub struct StageSetBuilder<Provider> {
48 stages: HashMap<StageId, StageEntry<Provider>>,
49 order: Vec<StageId>,
50}
5152impl<Provider> Defaultfor StageSetBuilder<Provider> {
53fn default() -> Self {
54Self { stages: HashMap::default(), order: Vec::new() }
55 }
56}
5758impl<Provider> Debugfor StageSetBuilder<Provider> {
59fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
60f.debug_struct("StageSetBuilder")
61 .field("stages", &self.stages)
62 .field("order", &self.order)
63 .finish()
64 }
65}
6667impl<Provider> StageSetBuilder<Provider> {
68fn index_of(&self, stage_id: StageId) -> usize {
69let index = self.order.iter().position(|&id| id == stage_id);
7071index.unwrap_or_else(|| panic!("Stage does not exist in set: {stage_id}"))
72 }
7374fn upsert_stage_state(&mut self, stage: Box<dyn Stage<Provider>>, added_at_index: usize) {
75let stage_id = stage.id();
76if self.stages.insert(stage.id(), StageEntry { stage, enabled: true }).is_some() {
77if let Some(to_remove) = self78.order
79 .iter()
80 .enumerate()
81 .find(|(i, id)| *i != added_at_index && **id == stage_id)
82 .map(|(i, _)| i)
83 {
84self.order.remove(to_remove);
85 }
86 }
87 }
8889/// Overrides the given [`Stage`], if it is in this set.
90 ///
91 /// # Panics
92 ///
93 /// Panics if the [`Stage`] is not in this set.
94pub fn set<S: Stage<Provider> + 'static>(mut self, stage: S) -> Self {
95let entry = self96.stages
97 .get_mut(&stage.id())
98 .unwrap_or_else(|| panic!("Stage does not exist in set: {}", stage.id()));
99entry.stage = Box::new(stage);
100self101}
102103/// Adds the given [`Stage`] at the end of this set.
104 ///
105 /// If the stage was already in the group, it is removed from its previous place.
106pub fn add_stage<S: Stage<Provider> + 'static>(mut self, stage: S) -> Self {
107let target_index = self.order.len();
108self.order.push(stage.id());
109self.upsert_stage_state(Box::new(stage), target_index);
110self111}
112113/// Adds the given [`Stage`] at the end of this set if it's [`Some`].
114 ///
115 /// If the stage was already in the group, it is removed from its previous place.
116pub fn add_stage_opt<S: Stage<Provider> + 'static>(self, stage: Option<S>) -> Self {
117if let Some(stage) = stage {
118self.add_stage(stage)
119 } else {
120self121}
122 }
123124/// Adds the given [`StageSet`] to the end of this set.
125 ///
126 /// If a stage is in both sets, it is removed from its previous place in this set. Because of
127 /// this, it is advisable to merge sets first and re-order stages after if needed.
128pub fn add_set<Set: StageSet<Provider>>(mut self, set: Set) -> Self {
129for stage in set.builder().build() {
130let target_index = self.order.len();
131self.order.push(stage.id());
132self.upsert_stage_state(stage, target_index);
133 }
134self135}
136137/// Adds the given [`Stage`] before the stage with the given [`StageId`].
138 ///
139 /// If the stage was already in the group, it is removed from its previous place.
140 ///
141 /// # Panics
142 ///
143 /// Panics if the dependency stage is not in this set.
144pub fn add_before<S: Stage<Provider> + 'static>(mut self, stage: S, before: StageId) -> Self {
145let target_index = self.index_of(before);
146self.order.insert(target_index, stage.id());
147self.upsert_stage_state(Box::new(stage), target_index);
148self149}
150151/// Adds the given [`Stage`] after the stage with the given [`StageId`].
152 ///
153 /// If the stage was already in the group, it is removed from its previous place.
154 ///
155 /// # Panics
156 ///
157 /// Panics if the dependency stage is not in this set.
158pub fn add_after<S: Stage<Provider> + 'static>(mut self, stage: S, after: StageId) -> Self {
159let target_index = self.index_of(after) + 1;
160self.order.insert(target_index, stage.id());
161self.upsert_stage_state(Box::new(stage), target_index);
162self163}
164165/// Enables the given stage.
166 ///
167 /// All stages within a [`StageSet`] are enabled by default.
168 ///
169 /// # Panics
170 ///
171 /// Panics if the stage is not in this set.
172pub fn enable(mut self, stage_id: StageId) -> Self {
173let entry =
174self.stages.get_mut(&stage_id).expect("Cannot enable a stage that is not in the set.");
175entry.enabled = true;
176self177}
178179/// Disables the given stage.
180 ///
181 /// The disabled [`Stage`] keeps its place in the set, so it can be used for ordering with
182 /// [`StageSetBuilder::add_before`] or [`StageSetBuilder::add_after`], or it can be re-enabled.
183 ///
184 /// All stages within a [`StageSet`] are enabled by default.
185 ///
186 /// # Panics
187 ///
188 /// Panics if the stage is not in this set.
189#[track_caller]
190pub fn disable(mut self, stage_id: StageId) -> Self {
191let entry = self192.stages
193 .get_mut(&stage_id)
194 .unwrap_or_else(|| panic!("Cannot disable a stage that is not in the set: {stage_id}"));
195entry.enabled = false;
196self197}
198199/// Disables all given stages. See [`disable`](Self::disable).
200 ///
201 /// If any of the stages is not in this set, it is ignored.
202pub fn disable_all(mut self, stages: &[StageId]) -> Self {
203for stage_id in stages {
204let Some(entry) = self.stages.get_mut(stage_id) else { continue };
205 entry.enabled = false;
206 }
207self208}
209210/// Disables the given stage if the given closure returns true.
211 ///
212 /// See [`Self::disable`]
213#[track_caller]
214pub fn disable_if<F>(self, stage_id: StageId, f: F) -> Self
215where
216F: FnOnce() -> bool,
217 {
218if f() {
219return self.disable(stage_id)
220 }
221self222}
223224/// Disables all given stages if the given closure returns true.
225 ///
226 /// See [`Self::disable`]
227#[track_caller]
228pub fn disable_all_if<F>(self, stages: &[StageId], f: F) -> Self
229where
230F: FnOnce() -> bool,
231 {
232if f() {
233return self.disable_all(stages)
234 }
235self236}
237238/// Consumes the builder and returns the contained [`Stage`]s in the order specified.
239pub fn build(mut self) -> Vec<Box<dyn Stage<Provider>>> {
240let mut stages = Vec::new();
241for id in &self.order {
242if let Some(entry) = self.stages.remove(id) {
243if entry.enabled {
244 stages.push(entry.stage);
245 }
246 }
247 }
248stages249 }
250}
251252impl<Provider> StageSet<Provider> for StageSetBuilder<Provider> {
253fn builder(self) -> Self {
254self255}
256}