fitnesstrax_lib/types/
timedistance.rs1use 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}