Skip to main content

syntax_workout_core/
fold.rs

1use crate::measure::Measure;
2use crate::node::Node;
3
4pub trait Fold {
5    fn fold_node(&mut self, node: Node) -> Node {
6        fold_node(self, node)
7    }
8    fn fold_measure(&mut self, measure: Measure) -> Measure {
9        measure
10    }
11}
12
13pub fn fold_node<F: Fold + ?Sized>(f: &mut F, mut node: Node) -> Node {
14    node.children = node.children
15        .into_iter()
16        .map(|child| f.fold_node(child))
17        .collect();
18
19    node.payload = match node.payload {
20        crate::node::NodePayload::Leaf { measures, intensity } => {
21            crate::node::NodePayload::Leaf {
22                measures: measures.into_iter().map(|m| f.fold_measure(m)).collect(),
23                intensity,
24            }
25        }
26        crate::node::NodePayload::Exercise { measures, intensity, rest_seconds } => {
27            crate::node::NodePayload::Exercise {
28                measures: measures.into_iter().map(|m| f.fold_measure(m)).collect(),
29                intensity,
30                rest_seconds,
31            }
32        }
33        other => other,
34    };
35
36    node
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::measure::{Measure, WeightUnit};
43    use crate::node::*;
44    use std::collections::BTreeMap;
45
46    struct KgToLbs;
47
48    impl Fold for KgToLbs {
49        fn fold_measure(&mut self, measure: Measure) -> Measure {
50            match measure {
51                Measure::Weight { amount, unit: WeightUnit::Kg } => {
52                    Measure::Weight { amount: amount * 2.20462, unit: WeightUnit::Lbs }
53                }
54                other => other,
55            }
56        }
57    }
58
59    #[test]
60    fn fold_converts_kg_to_lbs() {
61        let node = Node {
62            id: NodeId::from_string("s1"),
63            kind: NodeKind::Set,
64            name: None,
65            children: vec![],
66            payload: NodePayload::Leaf {
67                measures: vec![
68                    Measure::Weight { amount: 100.0, unit: WeightUnit::Kg },
69                    Measure::Reps(5),
70                ],
71                intensity: None,
72            },
73            metadata: BTreeMap::new(),
74        };
75
76        let mut folder = KgToLbs;
77        let result = folder.fold_node(node);
78
79        if let NodePayload::Leaf { measures, .. } = &result.payload {
80            match &measures[0] {
81                Measure::Weight { amount, unit } => {
82                    assert!((amount - 220.462).abs() < 0.01);
83                    assert_eq!(*unit, WeightUnit::Lbs);
84                }
85                _ => panic!("expected Weight"),
86            }
87            assert_eq!(measures[1], Measure::Reps(5));
88        } else {
89            panic!("expected Leaf");
90        }
91    }
92}