syntax_workout_core/
fold.rs1use 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}