syntax-workout-core 0.2.0

Workout tree algebra — represent any physical workout as a recursive tree
Documentation
use crate::measure::Measure;
use crate::node::Node;

pub trait Fold {
    fn fold_node(&mut self, node: Node) -> Node {
        fold_node(self, node)
    }
    fn fold_measure(&mut self, measure: Measure) -> Measure {
        measure
    }
}

pub fn fold_node<F: Fold + ?Sized>(f: &mut F, mut node: Node) -> Node {
    node.children = node.children
        .into_iter()
        .map(|child| f.fold_node(child))
        .collect();

    node.payload = match node.payload {
        crate::node::NodePayload::Leaf { measures, intensity } => {
            crate::node::NodePayload::Leaf {
                measures: measures.into_iter().map(|m| f.fold_measure(m)).collect(),
                intensity,
            }
        }
        crate::node::NodePayload::Exercise { measures, intensity, rest_seconds } => {
            crate::node::NodePayload::Exercise {
                measures: measures.into_iter().map(|m| f.fold_measure(m)).collect(),
                intensity,
                rest_seconds,
            }
        }
        other => other,
    };

    node
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::measure::{Measure, WeightUnit};
    use crate::node::*;
    use std::collections::BTreeMap;

    struct KgToLbs;

    impl Fold for KgToLbs {
        fn fold_measure(&mut self, measure: Measure) -> Measure {
            match measure {
                Measure::Weight { amount, unit: WeightUnit::Kg } => {
                    Measure::Weight { amount: amount * 2.20462, unit: WeightUnit::Lbs }
                }
                other => other,
            }
        }
    }

    #[test]
    fn fold_converts_kg_to_lbs() {
        let node = Node {
            id: NodeId::from_string("s1"),
            kind: NodeKind::Set,
            name: None,
            children: vec![],
            payload: NodePayload::Leaf {
                measures: vec![
                    Measure::Weight { amount: 100.0, unit: WeightUnit::Kg },
                    Measure::Reps(5),
                ],
                intensity: None,
            },
            metadata: BTreeMap::new(),
        };

        let mut folder = KgToLbs;
        let result = folder.fold_node(node);

        if let NodePayload::Leaf { measures, .. } = &result.payload {
            match &measures[0] {
                Measure::Weight { amount, unit } => {
                    assert!((amount - 220.462).abs() < 0.01);
                    assert_eq!(*unit, WeightUnit::Lbs);
                }
                _ => panic!("expected Weight"),
            }
            assert_eq!(measures[1], Measure::Reps(5));
        } else {
            panic!("expected Leaf");
        }
    }
}