diff --git a/crates/fj-core/src/objects/handles.rs b/crates/fj-core/src/objects/handles.rs index 1114a8f86..d9ee8b6a5 100644 --- a/crates/fj-core/src/objects/handles.rs +++ b/crates/fj-core/src/objects/handles.rs @@ -114,23 +114,52 @@ impl Handles { where T: Debug + Ord, { - let mut updated = Some(update(handle)); + self.replace(handle, |handle| [update(handle)]) + } + + /// Create a new instance in which the provided item has been replaced + /// + /// This is a more general version of [`Handles::update`] which can replace + /// a single item with multiple others. + /// + /// # Panics + /// + /// Panics, if the provided item is not present. + /// Panics, if the update results in a duplicate item. + #[must_use] + pub fn replace( + &self, + handle: &Handle, + replace: impl FnOnce(&Handle) -> [Handle; N], + ) -> Self + where + T: Debug + Ord, + { + let mut iter = self.iter().cloned().peekable(); + + // Collect all items before the item we want to update. + let mut before = Vec::new(); + loop { + let h = match iter.peek() { + Some(h) => h, + None => panic!("Item not found"), + }; - let items = self.iter().map(|h| { if h.id() == handle.id() { - updated - .take() - .expect("`Handles` should not contain same item twice") - } else { - h.clone() + // Found the item we want to update. Remove it from the + // iterator, then move on. + iter.next(); + break; } - }); - let handles = items.collect(); + let next = iter.next().expect("Peek just returned `Some`"); + before.push(next.clone()); + } - assert!(updated.is_none(), "Edge not found in cycle"); + let replaced = replace(handle); + let after = iter; - handles + before.into_iter().chain(replaced).chain(after).collect() } } diff --git a/crates/fj-core/src/operations/update/cycle.rs b/crates/fj-core/src/operations/update/cycle.rs index 3a67bf54b..ba34205e4 100644 --- a/crates/fj-core/src/operations/update/cycle.rs +++ b/crates/fj-core/src/operations/update/cycle.rs @@ -22,6 +22,23 @@ pub trait UpdateCycle { edge: &Handle, update: impl FnOnce(&Handle) -> Handle, ) -> Self; + + /// Replace the provided edge + /// + /// This is a more general version of [`UpdateCycle::update_edge`] which can + /// replace a single edge with multiple others. + /// + /// # Panics + /// + /// Uses [`Handles::update`] internally, and panics for the same reasons. + /// + /// [`Handles::update`]: crate::objects::Handles::update + #[must_use] + fn replace_edge( + &self, + edge: &Handle, + replace: impl FnOnce(&Handle) -> [Handle; N], + ) -> Self; } impl UpdateCycle for Cycle { @@ -38,4 +55,13 @@ impl UpdateCycle for Cycle { let edges = self.edges().update(edge, update); Cycle::new(edges) } + + fn replace_edge( + &self, + edge: &Handle, + replace: impl FnOnce(&Handle) -> [Handle; N], + ) -> Self { + let edges = self.edges().replace(edge, replace); + Cycle::new(edges) + } }