Skip to main content

tree_morph/
engine.rs

1//! Perform gradual rule-based transformations on trees.
2//!
3//! See the [`morph`](Engine::morph) for more information.
4
5use crate::cache::{CacheResult, RewriteCache};
6use crate::engine_zipper::{EngineZipper, NaiveZipper};
7use crate::events::EventHandlers;
8use crate::helpers::{SelectorFn, one_or_select};
9use crate::prelude::Rule;
10use crate::rule::{RuleGroups, RuleSet, apply_into_update};
11use crate::update::Update;
12
13use rayon::prelude::*;
14use tracing::{debug, error, info, instrument, trace};
15use uniplate::Uniplate;
16
17/// An engine for exhaustively transforming trees with user-defined rules.
18///
19/// See the [`morph`](Engine::morph) method for more information.
20pub struct Engine<T, M, R, C>
21where
22    T: Uniplate + Send + Sync,
23    R: Rule<T, M> + Clone,
24    C: RewriteCache<T>,
25{
26    pub(crate) event_handlers: EventHandlers<T, M, R>,
27
28    /// A collection of groups of equally-prioritised rules.
29    pub(crate) rule_groups: RuleGroups<T, M, R>,
30
31    pub(crate) selector: SelectorFn<T, M, R>,
32
33    pub(crate) cache: C,
34
35    /// Whether to use parallel (Rayon) iteration when checking rules.
36    pub(crate) parallel: bool,
37
38    pub(crate) fixedpoint: bool,
39
40    pub(crate) down_predicate: fn(&T) -> bool,
41}
42
43impl<T, M, R, C> Engine<T, M, R, C>
44where
45    T: Uniplate + Send + Sync,
46    R: Rule<T, M> + Clone + Sync,
47    M: Sync,
48    C: RewriteCache<T>,
49{
50    #[instrument(skip(selector, parallel, subtree, meta, rules, event_handlers))]
51    fn select_rule<'a>(
52        selector: SelectorFn<T, M, R>,
53        parallel: bool,
54        subtree: &T,
55        meta: &mut M,
56        rules: RuleSet<'a, R>,
57        event_handlers: &EventHandlers<T, M, R>,
58    ) -> Option<(&'a R, Update<T, M>)> {
59        trace!("Beginning Rule Checks");
60        if parallel {
61            let applicable: Vec<(&'a R, Update<T, M>)> = rules
62                .par_iter()
63                .filter_map(|rule| {
64                    let update = apply_into_update(rule, subtree, meta)?;
65                    Some((rule, update))
66                })
67                .collect();
68            trace!("Finished Rule Checks");
69            one_or_select(selector, subtree, &mut applicable.into_iter())
70        } else {
71            let applicable = &mut rules.into_iter().filter_map(|rule| {
72                event_handlers.trigger_before_rule(subtree, meta, rule);
73                let result = apply_into_update(rule, subtree, meta);
74                let is_applicable = result.is_some();
75                event_handlers.trigger_after_rule(subtree, meta, rule, is_applicable);
76                let update = result?;
77                Some((rule, update))
78            });
79            trace!("Finished Rule Checks");
80            one_or_select(selector, subtree, applicable)
81        }
82    }
83
84    fn apply_rule(
85        zipper: &mut EngineZipper<T, M, R, C>,
86        event_handlers: &EventHandlers<T, M, R>,
87        application: (&R, Update<T, M>),
88        level: usize,
89    ) {
90        let (rule, mut update) = application;
91        debug!("Applying Rule '{}'", rule.name());
92
93        let cache_active = zipper.cache.is_active();
94        let original = cache_active.then(|| zipper.focus().clone());
95
96        // Clear any stale cached hashes in the replacement subtree
97        zipper.cache.invalidate_subtree(&update.new_subtree);
98
99        let replacement = cache_active.then(|| update.new_subtree.clone());
100
101        // Replace the current subtree, invalidating subtree node states
102        zipper.replace_focus(update.new_subtree);
103
104        // Mark all ancestors as dirty, insert ancestor mappings, and move back to the root
105        zipper.mark_dirty_to_root(level);
106
107        let (focus, meta) = zipper.focus_and_meta();
108        let (new_tree, root_transformed) = update.commands.apply(focus.clone(), meta);
109
110        if root_transformed {
111            debug!("Root transformed, clearing state.");
112            // This must unfortunately throw all node states away,
113            // since the `transform` command may redefine the whole tree
114            zipper.replace_focus(new_tree);
115        } else if let (Some(orig), Some(repl)) = (original, replacement) {
116            if orig != repl {
117                zipper.cache.insert(&orig, Some(repl), level);
118            } else {
119                error!("Original Subtree is the same as Replacement Tree");
120            }
121        }
122
123        let (focus, meta) = zipper.focus_and_meta();
124        event_handlers.trigger_on_apply(focus, meta, rule);
125    }
126
127    fn apply_rule_fixedpoint(
128        zipper: &mut EngineZipper<T, M, R, C>,
129        event_handlers: &EventHandlers<T, M, R>,
130        rule_groups: &RuleGroups<T, M, R>,
131        selector: SelectorFn<T, M, R>,
132        parallel: bool,
133        application: (&R, Update<T, M>),
134        level: usize,
135    ) {
136        // Apply the initial rule
137        let (rule, mut update) = application;
138        debug!("Applying Rules in Fixed Point Mode");
139
140        if update.has_transform() {
141            trace!("Rule has Transform. Stopping Fixed Point");
142            Self::apply_rule(zipper, event_handlers, (rule, update), level);
143            return;
144        }
145
146        // No transform — apply locally
147        let cache_active = zipper.cache.is_active();
148        let original = cache_active.then(|| zipper.focus().clone());
149        zipper.cache.invalidate_subtree(&update.new_subtree);
150        let replacement = cache_active.then(|| update.new_subtree.clone());
151        zipper.replace_focus(update.new_subtree);
152
153        {
154            let (focus, meta) = zipper.focus_and_meta();
155            update.commands.apply(focus.clone(), meta);
156        }
157
158        if let (Some(orig), Some(repl)) = (original, replacement) {
159            if orig != repl {
160                zipper.cache.insert(&orig, Some(repl), level);
161            } else {
162                error!("Original Subtree is the same as Replacement Tree");
163            }
164        }
165
166        let (focus, meta) = zipper.focus_and_meta();
167        event_handlers.trigger_on_apply(focus, meta, rule);
168
169        let mut try_level = 0;
170        while try_level <= level {
171            let subtree = zipper.focus();
172            let id = rule_groups.discriminant_fn.map(|f| f(subtree));
173            let rules = rule_groups.get_rules(try_level, id);
174
175            let (subtree, meta) = zipper.focus_and_meta();
176            match Self::select_rule(selector, parallel, subtree, meta, rules, event_handlers) {
177                Some((rule, mut update)) => {
178                    if update.has_transform() {
179                        trace!("Rule has Transform. Stopping Fixed Point");
180                        Self::apply_rule(zipper, event_handlers, (rule, update), level);
181                        return;
182                    }
183
184                    debug!("Applying Rule '{}' till Fixed Point (local)", rule.name());
185                    let cache_active = zipper.cache.is_active();
186                    let original = cache_active.then(|| zipper.focus().clone());
187                    zipper.cache.invalidate_subtree(&update.new_subtree);
188                    let replacement = cache_active.then(|| update.new_subtree.clone());
189                    zipper.replace_focus(update.new_subtree);
190
191                    {
192                        let (focus, meta) = zipper.focus_and_meta();
193                        update.commands.apply(focus.clone(), meta);
194                    }
195
196                    if let (Some(orig), Some(repl)) = (original, replacement) {
197                        if orig != repl {
198                            zipper.cache.insert(&orig, Some(repl), level);
199                        } else {
200                            error!("Original Subtree is the same as Replacement Tree");
201                        }
202                    }
203
204                    let (focus, meta) = zipper.focus_and_meta();
205                    event_handlers.trigger_on_apply(focus, meta, rule);
206
207                    try_level = 0;
208                }
209                None => {
210                    try_level += 1;
211                }
212            }
213        }
214
215        // Node stabilized — mark pass-through and go to root
216        zipper.set_pass_through(level);
217        zipper.mark_dirty_to_root(level);
218    }
219
220    fn apply_rule_naive_fixedpoint(
221        zipper: &mut NaiveZipper<T, M, R, C>,
222        event_handlers: &EventHandlers<T, M, R>,
223        rule_groups: &RuleGroups<T, M, R>,
224        selector: SelectorFn<T, M, R>,
225        parallel: bool,
226        application: (&R, Update<T, M>),
227        level: usize,
228    ) -> bool {
229        let (rule, mut update) = application;
230        debug!("Applying Rule '{}' in Fast Mode (naive)", rule.name());
231
232        if update.has_transform() {
233            trace!("Rule has Transform. Stopping Fixed Point");
234            Self::apply_rule_naive(zipper, event_handlers, (rule, update), level);
235            return false;
236        }
237
238        // No transform — apply locally (no root traversal)
239        let cache_active = zipper.cache.is_active();
240        let original = cache_active.then(|| zipper.focus().clone());
241        zipper.cache.invalidate_subtree(&update.new_subtree);
242        let replacement = cache_active.then(|| update.new_subtree.clone());
243        zipper.replace_focus(update.new_subtree);
244
245        {
246            let (focus, meta) = zipper.focus_and_meta();
247            update.commands.apply(focus.clone(), meta);
248        }
249
250        if let (Some(orig), Some(repl)) = (original, replacement) {
251            if orig != repl {
252                zipper.cache.insert(&orig, Some(repl), level);
253            } else {
254                error!("Original Subtree is the same as Replacement Tree");
255            }
256        }
257
258        let (focus, meta) = zipper.focus_and_meta();
259        event_handlers.trigger_on_apply(focus, meta, rule);
260
261        // Try rules from level 0 up to current level
262        let mut try_level = 0;
263        while try_level <= level {
264            let subtree = zipper.focus();
265            let id = rule_groups.discriminant_fn.map(|f| f(subtree));
266            let rules = rule_groups.get_rules(try_level, id);
267
268            let (subtree, meta) = zipper.focus_and_meta();
269            match Self::select_rule(selector, parallel, subtree, meta, rules, event_handlers) {
270                Some((rule, mut update)) => {
271                    if update.has_transform() {
272                        trace!("Rule has Transform. Stopping Fixed Point");
273                        Self::apply_rule_naive(zipper, event_handlers, (rule, update), level);
274                        return false;
275                    }
276
277                    debug!(
278                        "Applying Rule '{}' in Fast Mode (naive, local)",
279                        rule.name()
280                    );
281                    let cache_active = zipper.cache.is_active();
282                    let original = cache_active.then(|| zipper.focus().clone());
283                    zipper.cache.invalidate_subtree(&update.new_subtree);
284                    let replacement = cache_active.then(|| update.new_subtree.clone());
285                    zipper.replace_focus(update.new_subtree);
286
287                    {
288                        let (focus, meta) = zipper.focus_and_meta();
289                        update.commands.apply(focus.clone(), meta);
290                    }
291
292                    if let (Some(orig), Some(repl)) = (original, replacement) {
293                        if orig != repl {
294                            zipper.cache.insert(&orig, Some(repl), level);
295                        } else {
296                            error!("Original Subtree is the same as Replacement Tree");
297                        }
298                    }
299
300                    let (focus, meta) = zipper.focus_and_meta();
301                    event_handlers.trigger_on_apply(focus, meta, rule);
302
303                    try_level = 0;
304                }
305                None => {
306                    try_level += 1;
307                }
308            }
309        }
310
311        // Node stabilized — need to go to root and rebuild ancestors
312        zipper.map_ancestors_to_root(level);
313        true
314    }
315
316    fn apply_rule_naive(
317        zipper: &mut NaiveZipper<T, M, R, C>,
318        event_handlers: &EventHandlers<T, M, R>,
319        application: (&R, Update<T, M>),
320        level: usize,
321    ) {
322        let (rule, mut update) = application;
323        debug!("Applying Rule '{}'", rule.name());
324
325        let cache_active = zipper.cache.is_active();
326        let original = cache_active.then(|| zipper.focus().clone());
327        zipper.cache.invalidate_subtree(&update.new_subtree);
328        let replacement = cache_active.then(|| update.new_subtree.clone());
329
330        zipper.replace_focus(update.new_subtree);
331        zipper.map_ancestors_to_root(level);
332
333        let (focus, meta) = zipper.focus_and_meta();
334        let (new_tree, root_transformed) = update.commands.apply(focus.clone(), meta);
335
336        if root_transformed {
337            trace!("Root transformed.");
338            zipper.replace_focus(new_tree);
339        } else if let (Some(orig), Some(repl)) = (original, replacement) {
340            if orig != repl {
341                zipper.cache.insert(&orig, Some(repl), level);
342            } else {
343                error!("Original Subtree is the same as Replacement Tree");
344            }
345        }
346
347        let (focus, meta) = zipper.focus_and_meta();
348        event_handlers.trigger_on_apply(focus, meta, rule);
349    }
350
351    /// Exhaustively rewrites a tree using user-defined rule groups.
352    ///
353    /// Rewriting is complete when all rules have been attempted with no change. Rules may be organised
354    /// into groups to control the order in which they are attempted.
355    ///
356    /// # Rule Groups
357    /// If all rules are treated equally, those which apply higher in the tree will take precedence
358    /// because of the left-most outer-most traversal order of the engine.
359    ///
360    /// This can cause problems if a rule which should ideally be applied early (e.g. evaluating
361    /// constant expressions) is left until later.
362    ///
363    /// To solve this, rules are organised into different collections or "groups".
364    /// The engine will apply rules in an earlier group to the entire tree before trying later groups.
365    /// That is, no rule is attempted if a rule in an earlier group is applicable to any part of the
366    /// tree.
367    ///
368    /// # Selector Functions
369    ///
370    /// If multiple rules in the same group are applicable to an expression, the user-defined
371    /// selector function is used to choose one. This function is given an iterator over pairs of
372    /// rules and the engine-created [`Update`] values which contain their modifications to the tree.
373    ///
374    /// Some useful selector functions are available in the [`helpers`](crate::helpers) module. One
375    /// common function is [`select_first`](crate::helpers::select_first), which simply returns the
376    /// first applicable rule.
377    ///
378    /// # Event Handlers
379    ///
380    /// The engine provides events for more fine-tuned control over rewriting behaviour. Events have mutable
381    /// access to the current metadata.
382    ///
383    /// The engine will call the provided handlers for the "enter" and
384    /// "exits" events as it enters and exits subtrees while traversing, respectively.
385    ///
386    /// The "enter" event is triggered first on the root, and then whenever the engine moves down
387    /// into a subtree. As a result, when a node is passed to rules, all nodes above it will have
388    /// been passed to handlers for this event, in ascending order of their proximity to the root.
389    ///
390    /// The "exit" event is triggered when the engine leaves a subtree.
391    /// All nodes passed to "enter" event handlers (except the root) will also be passed to "exit"
392    /// event handlers in reverse order.
393    ///
394    /// In effect, before a node is passed to rules, all nodes in the path from the root (including the
395    /// current node) will have been passed to the "enter" event in order. No nodes are skipped.
396    ///
397    /// Event handling is useful when, for example, using a symbol table to keep track of variable definitions.
398    /// When entering a scope where a variable is defined, one can place the variable and its value into the table.
399    /// That stack can then be used for quick value lookup while inside the scope. When leaving the scope the
400    /// variable can be removed from the table.
401    ///
402    /// # Optimizations
403    ///
404    /// To optimize the morph function, we use a dirty/clean approach. Since whether a rule applies
405    /// is purely a function of a node and its children, rules should only be checked once unless a node
406    /// or one of its children has been changed.
407    ///
408    /// # Example
409    /// ```rust
410    /// use tree_morph::{prelude::*, helpers::select_panic};
411    /// use uniplate::Uniplate;
412    ///
413    ///
414    /// // A simple language of multiplied and squared constant expressions
415    /// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
416    /// #[uniplate()]
417    /// enum Expr {
418    ///     Val(i32),
419    ///     Mul(Box<Expr>, Box<Expr>),
420    ///     Sqr(Box<Expr>),
421    /// }
422    ///
423    /// // a * b ~> (value of) a * b, where 'a' and 'b' are literal values
424    /// fn rule_eval_mul(cmds: &mut Commands<Expr, i32>, subtree: &Expr, meta: &i32) -> Option<Expr> {
425    ///     cmds.mut_meta(Box::new(|m: &mut i32| *m += 1));
426    ///
427    ///     if let Expr::Mul(a, b) = subtree {
428    ///         if let (Expr::Val(a_v), Expr::Val(b_v)) = (a.as_ref(), b.as_ref()) {
429    ///             return Some(Expr::Val(a_v * b_v));
430    ///         }
431    ///     }
432    ///     None
433    /// }
434    ///
435    /// // e ^ 2 ~> e * e, where e is an expression
436    /// // If this rule is applied before the sub-expression is fully evaluated, duplicate work
437    /// // will be done on the resulting two identical sub-expressions.
438    /// fn rule_expand_sqr(cmds: &mut Commands<Expr, i32>, subtree: &Expr, meta: &i32) -> Option<Expr> {
439    ///     cmds.mut_meta(Box::new(|m: &mut i32| *m += 1));
440    ///
441    ///     if let Expr::Sqr(expr) = subtree {
442    ///         return Some(Expr::Mul(
443    ///             Box::new(*expr.clone()),
444    ///             Box::new(*expr.clone())
445    ///         ));
446    ///     }
447    ///     None
448    /// }
449    ///
450    /// // (1 * 2) ^ 2
451    /// let expr = Expr::Sqr(
452    ///     Box::new(Expr::Mul(
453    ///         Box::new(Expr::Val(1)),
454    ///         Box::new(Expr::Val(2))
455    ///     ))
456    /// );
457    ///
458    /// // Try with both rules in the same group, keeping track of the number of rule applications
459    /// let mut engine = EngineBuilder::new()
460    ///     .set_selector(select_panic)
461    ///     .add_rule_group(rule_fns![rule_eval_mul, rule_expand_sqr])
462    ///     .build();
463    /// let (result, num_applications) = engine.morph(expr.clone(), 0);
464    /// assert_eq!(result, Expr::Val(4));
465    /// assert_eq!(num_applications, 4); // The `Sqr` is expanded first, causing duplicate work
466    ///
467    /// // Move the evaluation rule to an earlier group
468    /// let mut engine = EngineBuilder::new()
469    ///     .set_selector(select_panic)
470    ///     .add_rule_group(rule_fns![rule_eval_mul])
471    ///     .add_rule_group(rule_fns![rule_expand_sqr])
472    ///     .build();
473    /// let (result, num_applications) = engine.morph(expr.clone(), 0);
474    /// assert_eq!(result, Expr::Val(4));
475    /// assert_eq!(num_applications, 3); // Now the sub-expression (1 * 2) is evaluated first
476    /// ```
477    // #[instrument(skip(self, tree, meta))]
478    pub fn morph(&mut self, tree: T, meta: M) -> (T, M)
479    where
480        T: Uniplate + Send + Sync,
481        R: Rule<T, M>,
482    {
483        // Owns the tree/meta and is consumed to get them back at the end
484        let mut zipper = EngineZipper::new(
485            tree,
486            meta,
487            self.down_predicate,
488            &self.event_handlers,
489            &mut self.cache,
490        );
491        info!("Beginning Morph");
492
493        'main: loop {
494            // Return here after every successful rule application
495            for level in 0..self.rule_groups.levels() {
496                while zipper.go_next_dirty(level).is_some() {
497                    trace!("Got Dirty, Level {}", level);
498
499                    {
500                        let subtree = zipper.focus();
501                        let id = self.rule_groups.discriminant_fn.map(|f| f(subtree));
502                        let rules = self.rule_groups.get_rules(level, id);
503
504                        debug!("Checking Level {} with {} Rules", level, rules.len());
505                        match zipper.cache.get(subtree, level) {
506                            CacheResult::Terminal(clean_level) => {
507                                debug!(
508                                    "Cache Hit - Nothing Applicable (clean through level {})",
509                                    clean_level
510                                );
511                                zipper.trigger_cache_hit();
512                                zipper.set_dirty_from(clean_level + 1);
513                                continue;
514                            }
515                            CacheResult::Rewrite(cached) => {
516                                debug!("Cache Hit");
517                                zipper.trigger_cache_hit();
518                                zipper.replace_focus(cached);
519                                zipper.mark_dirty_to_root(level);
520                                continue 'main;
521                            }
522                            _ => {
523                                zipper.trigger_cache_miss();
524                            }
525                        };
526                    }
527
528                    // Choose one transformation from all applicable rules at this level
529                    let (subtree, meta) = zipper.focus_and_meta();
530                    let id = self.rule_groups.discriminant_fn.map(|f| f(subtree));
531
532                    let rules = self.rule_groups.get_rules(level, id);
533                    match Self::select_rule(
534                        self.selector,
535                        self.parallel,
536                        subtree,
537                        meta,
538                        rules,
539                        &self.event_handlers,
540                    ) {
541                        Some(selected) => {
542                            if self.fixedpoint {
543                                Self::apply_rule_fixedpoint(
544                                    &mut zipper,
545                                    &self.event_handlers,
546                                    &self.rule_groups,
547                                    self.selector,
548                                    self.parallel,
549                                    selected,
550                                    level,
551                                );
552                            } else {
553                                Self::apply_rule(
554                                    &mut zipper,
555                                    &self.event_handlers,
556                                    selected,
557                                    level,
558                                );
559                            }
560                            continue 'main;
561                        }
562                        None => {
563                            trace!("Nothing Applicable");
564                            if zipper.cache.is_active() {
565                                let subtree = zipper.focus().clone();
566                                zipper.cache.insert(&subtree, None, level);
567                            }
568                            zipper.set_dirty_from(level + 1);
569                        }
570                    }
571                }
572            }
573
574            // All rules have been tried with no more changes
575            break;
576        }
577
578        info!("Finished Morph");
579        zipper.into()
580    }
581
582    /// Exhaustively rewrites a tree using user-defined rule groups.
583    ///
584    /// It is not recommended to use this function besides testing and for correctness.
585    pub fn morph_naive(&mut self, tree: T, meta: M) -> (T, M)
586    where
587        T: Uniplate,
588        R: Rule<T, M>,
589    {
590        let mut zipper = NaiveZipper::new(
591            tree,
592            meta,
593            self.down_predicate,
594            &self.event_handlers,
595            &mut self.cache,
596        );
597        info!("Beginning Naive Morph");
598
599        'main: loop {
600            for level in 0..self.rule_groups.levels() {
601                loop {
602                    {
603                        let subtree = zipper.focus();
604                        match zipper.cache.get(subtree, level) {
605                            CacheResult::Terminal(_clean_level) => {
606                                debug!("Cache Hit - Nothing Applicable");
607                                zipper.trigger_cache_hit();
608                                if zipper.get_next().is_none() {
609                                    break;
610                                }
611                                continue;
612                            }
613                            CacheResult::Rewrite(cached) => {
614                                debug!("Cache Hit");
615                                zipper.trigger_cache_hit();
616                                zipper.replace_focus(cached);
617                                zipper.map_ancestors_to_root(level);
618                                continue 'main;
619                            }
620                            _ => {
621                                zipper.trigger_cache_miss();
622                            }
623                        };
624                    }
625
626                    let (subtree, meta) = zipper.focus_and_meta();
627                    let id = self.rule_groups.discriminant_fn.map(|f| f(subtree));
628                    let rules = self.rule_groups.get_rules(level, id);
629                    debug!("Checking Level {} with {} Rules", level, rules.len());
630                    // Choose one transformation from all applicable rules at this level
631                    let selected = Self::select_rule(
632                        self.selector,
633                        self.parallel,
634                        subtree,
635                        meta,
636                        rules,
637                        &self.event_handlers,
638                    );
639
640                    if let Some(selected) = selected {
641                        if self.fixedpoint {
642                            let stabilized = Self::apply_rule_naive_fixedpoint(
643                                &mut zipper,
644                                &self.event_handlers,
645                                &self.rule_groups,
646                                self.selector,
647                                self.parallel,
648                                selected,
649                                level,
650                            );
651                            if !stabilized {
652                                continue 'main;
653                            }
654                            // Stabilized — continue 'main to restart from root
655                            continue 'main;
656                        } else {
657                            Self::apply_rule_naive(
658                                &mut zipper,
659                                &self.event_handlers,
660                                selected,
661                                level,
662                            );
663                        }
664                        continue 'main;
665                    } else {
666                        debug!("Nothing Applicable");
667                        if zipper.cache.is_active() {
668                            let subtree = zipper.focus().clone();
669                            zipper.cache.insert(&subtree, None, level);
670                        }
671                    }
672
673                    if zipper.get_next().is_none() {
674                        break;
675                    }
676                }
677            }
678
679            break;
680        }
681
682        info!("Finished Naive Morph");
683        zipper.into_parts()
684    }
685}