1use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
9#[serde(into = "JsonFuchsiaPerfBenchmarkResult", try_from = "JsonFuchsiaPerfBenchmarkResult")]
10pub struct FuchsiaPerfBenchmarkResult {
11 pub label: String,
12 pub test_suite: String,
13 pub unit: Unit,
14 pub direction: Direction,
15 pub values: Vec<f64>,
16}
17
18#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
20pub enum Unit {
21 #[serde(rename = "ns")]
22 #[serde(alias = "nanoseconds")]
23 Nanoseconds,
24 #[serde(rename = "ms")]
25 #[serde(alias = "milliseconds")]
26 Milliseconds,
27 #[serde(rename = "bytes")]
28 Bytes,
29 #[serde(rename = "bytes/second")]
30 BytesPerSecond,
31 #[serde(rename = "frames/second")]
32 FramesPerSecond,
33 #[serde(rename = "percent")]
34 Percent,
35 #[serde(rename = "count")]
36 Count,
37 #[serde(rename = "W")]
38 Watts,
39}
40
41impl std::fmt::Display for Unit {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 Unit::Nanoseconds => write!(f, "ns"),
45 Unit::Milliseconds => write!(f, "ms"),
46 Unit::Bytes => write!(f, "bytes"),
47 Unit::BytesPerSecond => write!(f, "bytes/second"),
48 Unit::FramesPerSecond => write!(f, "frames/second"),
49 Unit::Percent => write!(f, "percent"),
50 Unit::Count => write!(f, "count"),
51 Unit::Watts => write!(f, "W"),
52 }
53 }
54}
55
56impl std::str::FromStr for Unit {
57 type Err = String;
58
59 fn from_str(s: &str) -> Result<Self, Self::Err> {
60 match s {
61 "ns" | "nanoseconds" => Ok(Unit::Nanoseconds),
62 "ms" | "milliseconds" => Ok(Unit::Milliseconds),
63 "bytes" => Ok(Unit::Bytes),
64 "bytes/second" => Ok(Unit::BytesPerSecond),
65 "frames/second" => Ok(Unit::FramesPerSecond),
66 "percent" => Ok(Unit::Percent),
67 "count" => Ok(Unit::Count),
68 "W" => Ok(Unit::Watts),
69 _ => Err(format!("Invalid benchmark unit: {}", s)),
70 }
71 }
72}
73
74#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
77pub enum Direction {
78 #[serde(rename = "biggerIsBetter")]
79 BiggerBetter,
80 #[serde(rename = "smallerIsBetter")]
81 SmallerBetter,
82}
83
84impl std::fmt::Display for Direction {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 match self {
87 Direction::BiggerBetter => write!(f, "biggerIsBetter"),
88 Direction::SmallerBetter => write!(f, "smallerIsBetter"),
89 }
90 }
91}
92
93impl std::str::FromStr for Direction {
94 type Err = String;
95
96 fn from_str(s: &str) -> Result<Self, Self::Err> {
97 match s {
98 "biggerIsBetter" => Ok(Direction::BiggerBetter),
99 "smallerIsBetter" => Ok(Direction::SmallerBetter),
100 _ => Err(format!("Invalid benchmark direction: {}", s)),
101 }
102 }
103}
104
105#[derive(Deserialize, Serialize)]
106struct JsonFuchsiaPerfBenchmarkResult {
107 label: String,
108 test_suite: String,
109 unit: String,
110 values: Vec<f64>,
111}
112
113impl From<FuchsiaPerfBenchmarkResult> for JsonFuchsiaPerfBenchmarkResult {
114 fn from(result: FuchsiaPerfBenchmarkResult) -> Self {
115 let unit = format!("{}_{}", result.unit, result.direction);
116 JsonFuchsiaPerfBenchmarkResult {
117 label: result.label,
118 test_suite: result.test_suite,
119 unit,
120 values: result.values,
121 }
122 }
123}
124
125impl TryFrom<JsonFuchsiaPerfBenchmarkResult> for FuchsiaPerfBenchmarkResult {
126 type Error = String;
127
128 fn try_from(json: JsonFuchsiaPerfBenchmarkResult) -> Result<Self, Self::Error> {
129 let (unit, direction) = if let Some((u, d)) = json.unit.rsplit_once('_') {
130 (u.parse()?, d.parse()?)
131 } else {
132 let u: Unit = json.unit.parse()?;
133 let d = match u {
134 Unit::Nanoseconds
135 | Unit::Milliseconds
136 | Unit::Bytes
137 | Unit::Count
138 | Unit::Watts => Direction::SmallerBetter,
139 Unit::BytesPerSecond | Unit::FramesPerSecond | Unit::Percent => {
140 Direction::BiggerBetter
141 }
142 };
143 (u, d)
144 };
145
146 Ok(FuchsiaPerfBenchmarkResult {
147 label: json.label,
148 test_suite: json.test_suite,
149 unit,
150 direction,
151 values: json.values,
152 })
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use serde_json::json;
160
161 #[test]
162 fn test_serialization_with_direction() {
163 let result = FuchsiaPerfBenchmarkResult {
164 label: "test_label".to_string(),
165 test_suite: "test_suite".to_string(),
166 unit: Unit::Nanoseconds,
167 direction: Direction::SmallerBetter,
168 values: vec![1.0, 2.0],
169 };
170
171 let json = serde_json::to_value(&result).unwrap();
172 let expected = json!({
173 "label": "test_label",
174 "test_suite": "test_suite",
175 "unit": "ns_smallerIsBetter",
176 "values": [1.0, 2.0]
177 });
178
179 assert_eq!(json, expected);
180 }
181
182 #[test]
183 fn test_serialization_with_bigger_better() {
184 let result = FuchsiaPerfBenchmarkResult {
185 label: "test_label".to_string(),
186 test_suite: "test_suite".to_string(),
187 unit: Unit::BytesPerSecond,
188 direction: Direction::BiggerBetter,
189 values: vec![1.0, 2.0],
190 };
191
192 let json = serde_json::to_value(&result).unwrap();
193 let expected = json!({
194 "label": "test_label",
195 "test_suite": "test_suite",
196 "unit": "bytes/second_biggerIsBetter",
197 "values": [1.0, 2.0]
198 });
199
200 assert_eq!(json, expected);
201 }
202
203 #[test]
204 fn test_deserialization_with_direction() {
205 let json = json!({
206 "label": "test_label",
207 "test_suite": "test_suite",
208 "unit": "ns_smallerIsBetter",
209 "values": [1.0, 2.0]
210 });
211
212 let result: FuchsiaPerfBenchmarkResult = serde_json::from_value(json).unwrap();
213 assert_eq!(result.unit, Unit::Nanoseconds);
214 assert_eq!(result.direction, Direction::SmallerBetter);
215 }
216
217 #[test]
218 fn test_deserialization_without_direction() {
219 let json = json!({
220 "label": "test_label",
221 "test_suite": "test_suite",
222 "unit": "ns",
223 "values": [1.0, 2.0]
224 });
225
226 let result: FuchsiaPerfBenchmarkResult = serde_json::from_value(json).unwrap();
227 assert_eq!(result.unit, Unit::Nanoseconds);
228 assert_eq!(result.direction, Direction::SmallerBetter);
229 }
230}