fitnesstrax_lib/types/
timedistance.rs

1use dimensioned::si::{Meter, Second};
2use emseries::{DateTimeTz, Recordable};
3use std::convert::TryFrom;
4
5#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
6pub enum ActivityType {
7    Cycling,
8    Rowing,
9    Running,
10    Swimming,
11    Walking,
12}
13
14pub fn activity_types() -> Vec<ActivityType> {
15    vec![
16        ActivityType::Cycling,
17        ActivityType::Rowing,
18        ActivityType::Running,
19        ActivityType::Swimming,
20        ActivityType::Walking,
21    ]
22}
23
24impl TryFrom<&str> for ActivityType {
25    type Error = &'static str;
26
27    fn try_from(inp: &str) -> Result<ActivityType, Self::Error> {
28        match inp {
29            "Cycling" => Ok(ActivityType::Cycling),
30            "Rowing" => Ok(ActivityType::Rowing),
31            "Running" => Ok(ActivityType::Running),
32            "Swimming" => Ok(ActivityType::Swimming),
33            "Walking" => Ok(ActivityType::Walking),
34            _ => Err("invalid activity string"),
35        }
36    }
37}
38
39#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
40pub struct TimeDistanceRecord {
41    #[serde(rename = "date")]
42    pub timestamp: DateTimeTz,
43    pub activity: ActivityType,
44    pub distance: Option<Meter<f64>>,
45    pub duration: Option<Second<f64>>,
46    pub comments: Option<String>,
47}
48
49impl TimeDistanceRecord {
50    pub fn new(
51        timestamp: DateTimeTz,
52        activity: ActivityType,
53        distance: Option<Meter<f64>>,
54        duration: Option<Second<f64>>,
55        comments: Option<String>,
56    ) -> TimeDistanceRecord {
57        TimeDistanceRecord {
58            timestamp,
59            activity,
60            distance,
61            duration,
62            comments,
63        }
64    }
65}
66
67impl Recordable for TimeDistanceRecord {
68    fn timestamp(&self) -> DateTimeTz {
69        self.timestamp.clone()
70    }
71
72    fn tags(&self) -> Vec<String> {
73        match self.activity {
74            ActivityType::Cycling => vec![String::from("Cycling")],
75            ActivityType::Rowing => vec![String::from("Rowing")],
76            ActivityType::Running => vec![String::from("Running")],
77            ActivityType::Swimming => vec![String::from("Swimming")],
78            ActivityType::Walking => vec![String::from("Walking")],
79        }
80    }
81}
82
83#[cfg(test)]
84mod test {
85    use super::{ActivityType, TimeDistanceRecord};
86    use dimensioned::si::{M, S};
87
88    #[test]
89    pub fn deserialize_time_distance() {
90        let cycling_track_str = "{\"distance\":12200,\"date\":\"2017-10-28T19:27:00Z\",\"activity\":\"Cycling\",\"comments\":null,\"duration\":3120}";
91        let cycle_track: TimeDistanceRecord = serde_json::from_str(cycling_track_str).unwrap();
92        assert_eq!(cycle_track.activity, ActivityType::Cycling);
93        assert_eq!(cycle_track.distance, Some(12200. * M));
94        assert_eq!(cycle_track.duration, Some(3120. * S));
95
96        let running_track_str = "{\"distance\":3630,\"date\":\"2018-11-12T18:30:00Z\",\"activity\":\"Running\",\"comments\":null,\"duration\":1800}";
97        let running_track: Result<TimeDistanceRecord, serde_json::Error> =
98            serde_json::from_str(running_track_str);
99        match running_track {
100            Ok(track) => {
101                assert_eq!(track.activity, ActivityType::Running);
102                assert_eq!(track.distance, Some(3630. * M));
103                assert_eq!(track.duration, Some(1800. * S));
104            }
105            Err(err) => panic!(err),
106        }
107    }
108}