1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
9pub struct Response<D> {
10 pub data: D,
12 pub jsonapi: APIVersion,
14 #[serde(default)]
16 pub links: Option<Links>,
17}
18
19#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
21pub struct APIVersion {
22 pub version: String,
24}
25
26#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
28pub struct Links {
29 #[serde(default)]
31 pub first: Option<String>,
32 #[serde(default)]
34 pub next: Option<String>,
35 #[serde(default)]
37 pub last: Option<String>,
38}
39
40#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
42pub struct Resource<Attribute> {
43 #[serde(rename = "type")]
45 pub resource_type: String,
46 pub id: String,
48 #[serde(default)]
50 pub links: Option<HashMap<String, String>>,
51 pub attributes: Attribute,
53 #[serde(default)]
55 pub relationships: Option<HashMap<String, Relationships>>,
56}
57
58#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
60pub struct Relationships {
61 pub data: Option<RelationshipAtom>,
63}
64
65#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
67pub struct RelationshipAtom {
68 #[serde(rename = "type")]
70 pub relationship_type: String,
71 pub id: String,
73}
74
75#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Copy)]
77#[serde(try_from = "u8")]
78#[serde(into = "u8")]
79pub enum RouteType {
80 LightRail,
82 HeavyRail,
84 CommuterRail,
86 Bus,
88 Ferry,
90}
91
92impl TryFrom<u8> for RouteType {
93 type Error = String;
94
95 fn try_from(value: u8) -> Result<Self, Self::Error> {
96 match value {
97 0 => Ok(Self::LightRail),
98 1 => Ok(Self::HeavyRail),
99 2 => Ok(Self::CommuterRail),
100 3 => Ok(Self::Bus),
101 4 => Ok(Self::Ferry),
102 _ => Err(format!("invalid route type value: {}", value)),
103 }
104 }
105}
106
107impl From<RouteType> for u8 {
108 fn from(value: RouteType) -> u8 {
109 match value {
110 RouteType::LightRail => 0,
111 RouteType::HeavyRail => 1,
112 RouteType::CommuterRail => 2,
113 RouteType::Bus => 3,
114 RouteType::Ferry => 4,
115 }
116 }
117}
118
119#[derive(Debug, PartialEq, Clone, Copy, Deserialize, Serialize)]
121#[serde(try_from = "u8")]
122#[serde(into = "u8")]
123pub enum WheelchairAccessible {
124 NoInfo,
126 Accessible,
128 Inaccessible,
130}
131
132impl TryFrom<u8> for WheelchairAccessible {
133 type Error = String;
134
135 fn try_from(value: u8) -> Result<Self, Self::Error> {
136 match value {
137 0 => Ok(Self::NoInfo),
138 1 => Ok(Self::Accessible),
139 2 => Ok(Self::Inaccessible),
140 _ => Err(format!("invalid wheelchair accessibility value: {}", value)),
141 }
142 }
143}
144
145impl From<WheelchairAccessible> for u8 {
146 fn from(value: WheelchairAccessible) -> Self {
147 match value {
148 WheelchairAccessible::NoInfo => 0,
149 WheelchairAccessible::Accessible => 1,
150 WheelchairAccessible::Inaccessible => 2,
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 use rstest::*;
160
161 #[rstest]
162 #[case::zero(0, Ok(RouteType::LightRail))]
163 #[case::one(1, Ok(RouteType::HeavyRail))]
164 #[case::two(2, Ok(RouteType::CommuterRail))]
165 #[case::three(3, Ok(RouteType::Bus))]
166 #[case::four(4, Ok(RouteType::Ferry))]
167 #[case::invalid(5, Err("invalid route type value: 5".into()))]
168 fn test_route_type_try_from_u8(#[case] input: u8, #[case] expected: Result<RouteType, String>) {
169 assert_eq!(RouteType::try_from(input), expected);
170 }
171
172 #[rstest]
173 #[case::light_rail(RouteType::LightRail, 0)]
174 #[case::heavy_rail(RouteType::HeavyRail, 1)]
175 #[case::commuter_rail(RouteType::CommuterRail, 2)]
176 #[case::bus(RouteType::Bus, 3)]
177 #[case::ferry(RouteType::Ferry, 4)]
178 fn test_u8_from_route_type(#[case] input: RouteType, #[case] expected: u8) {
179 assert_eq!(u8::from(input), expected);
180 }
181
182 #[rstest]
183 #[case::zero(0, Ok(WheelchairAccessible::NoInfo))]
184 #[case::one(1, Ok(WheelchairAccessible::Accessible))]
185 #[case::two(2, Ok(WheelchairAccessible::Inaccessible))]
186 #[case::invalid(3, Err("invalid wheelchair accessibility value: 3".into()))]
187 fn test_wheelchair_accessible_try_from_u8(#[case] input: u8, #[case] expected: Result<WheelchairAccessible, String>) {
188 assert_eq!(WheelchairAccessible::try_from(input), expected);
189 }
190
191 #[rstest]
192 #[case::no_info(WheelchairAccessible::NoInfo, 0)]
193 #[case::accessible(WheelchairAccessible::Accessible, 1)]
194 #[case::inaccessible(WheelchairAccessible::Inaccessible, 2)]
195 fn test_u8_from_wheelchair_accessible(#[case] input: WheelchairAccessible, #[case] expected: u8) {
196 assert_eq!(u8::from(input), expected);
197 }
198}