Skip to content

Commit 4cc3da7

Browse files
code-inflationrobatscandit
authored andcommitted
add metadata to json output (#247)
1 parent 370023a commit 4cc3da7

File tree

2 files changed

+92
-27
lines changed

2 files changed

+92
-27
lines changed

src/measurements.rs

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::boxplot;
2+
use crate::speedtest::Metadata;
23
use crate::speedtest::TestType;
34
use crate::OutputFormat;
45
use indexmap::IndexSet;
@@ -50,6 +51,7 @@ pub(crate) fn log_measurements(
5051
payload_sizes: Vec<usize>,
5152
verbose: bool,
5253
output_format: OutputFormat,
54+
metadata: Option<&Metadata>,
5355
) {
5456
if output_format == OutputFormat::StdOut {
5557
println!("\nSummary Statistics");
@@ -79,32 +81,12 @@ pub(crate) fn log_measurements(
7981
wtr.flush().unwrap();
8082
}
8183
OutputFormat::Json => {
82-
let mut output = serde_json::Map::new();
83-
output.insert(
84-
"speed_measurements".to_string(),
85-
serde_json::to_value(&stat_measurements).unwrap(),
86-
);
87-
if let Some(latency) = latency_measurement {
88-
output.insert(
89-
"latency_measurement".to_string(),
90-
serde_json::to_value(latency).unwrap(),
91-
);
92-
}
84+
let output = compose_output_json(&stat_measurements, latency_measurement, metadata);
9385
serde_json::to_writer(io::stdout(), &output).unwrap();
9486
println!();
9587
}
9688
OutputFormat::JsonPretty => {
97-
let mut output = serde_json::Map::new();
98-
output.insert(
99-
"speed_measurements".to_string(),
100-
serde_json::to_value(&stat_measurements).unwrap(),
101-
);
102-
if let Some(latency) = latency_measurement {
103-
output.insert(
104-
"latency_measurement".to_string(),
105-
serde_json::to_value(latency).unwrap(),
106-
);
107-
}
89+
let output = compose_output_json(&stat_measurements, latency_measurement, metadata);
10890
serde_json::to_writer_pretty(io::stdout(), &output).unwrap();
10991
println!();
11092
}
@@ -113,6 +95,31 @@ pub(crate) fn log_measurements(
11395
}
11496
}
11597

98+
fn compose_output_json(
99+
stat_measurements: &[StatMeasurement],
100+
latency_measurement: Option<&LatencyMeasurement>,
101+
metadata: Option<&Metadata>,
102+
) -> serde_json::Map<String, serde_json::Value> {
103+
let mut output = serde_json::Map::new();
104+
output.insert(
105+
"speed_measurements".to_string(),
106+
serde_json::to_value(stat_measurements).unwrap(),
107+
);
108+
if let Some(latency) = latency_measurement {
109+
output.insert(
110+
"latency_measurement".to_string(),
111+
serde_json::to_value(latency).unwrap(),
112+
);
113+
}
114+
if let Some(metadata) = metadata {
115+
output.insert(
116+
"metadata".to_string(),
117+
serde_json::to_value(metadata).unwrap(),
118+
);
119+
}
120+
output
121+
}
122+
116123
fn log_measurements_by_test_type(
117124
measurements: &[Measurement],
118125
payload_sizes: Vec<usize>,
@@ -317,4 +324,60 @@ mod tests {
317324
fn test_median_single_value() {
318325
assert_eq!(median(&[5.0]), 5.0);
319326
}
327+
328+
#[test]
329+
fn test_compose_output_json_includes_metadata() {
330+
let stat_measurements = vec![StatMeasurement {
331+
test_type: TestType::Download,
332+
payload_size: 100_000,
333+
min: 1.0,
334+
q1: 1.5,
335+
median: 2.0,
336+
q3: 2.5,
337+
max: 3.0,
338+
avg: 2.0,
339+
}];
340+
let latency = LatencyMeasurement {
341+
avg_latency_ms: 10.0,
342+
min_latency_ms: 9.0,
343+
max_latency_ms: 11.0,
344+
latency_measurements: vec![9.0, 10.0, 11.0],
345+
};
346+
let metadata = Metadata {
347+
city: "City".to_string(),
348+
country: "Country".to_string(),
349+
ip: "127.0.0.1".to_string(),
350+
asn: "ASN".to_string(),
351+
colo: "ABC".to_string(),
352+
};
353+
354+
let output =
355+
super::compose_output_json(&stat_measurements, Some(&latency), Some(&metadata));
356+
357+
let metadata_value = output.get("metadata").expect("metadata missing");
358+
let metadata_obj = metadata_value.as_object().expect("metadata not an object");
359+
assert_eq!(
360+
metadata_obj.get("city").and_then(|v| v.as_str()),
361+
Some("City")
362+
);
363+
assert_eq!(
364+
metadata_obj.get("country").and_then(|v| v.as_str()),
365+
Some("Country")
366+
);
367+
assert_eq!(
368+
metadata_obj.get("ip").and_then(|v| v.as_str()),
369+
Some("127.0.0.1")
370+
);
371+
assert_eq!(
372+
metadata_obj.get("asn").and_then(|v| v.as_str()),
373+
Some("ASN")
374+
);
375+
assert_eq!(
376+
metadata_obj.get("colo").and_then(|v| v.as_str()),
377+
Some("ABC")
378+
);
379+
380+
assert!(output.get("latency_measurement").is_some());
381+
assert!(output.get("speed_measurements").is_some());
382+
}
320383
}

src/speedtest.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,13 @@ impl PayloadSize {
6565
}
6666
}
6767

68+
#[derive(Clone, Debug, Serialize)]
6869
pub struct Metadata {
69-
city: String,
70-
country: String,
71-
ip: String,
72-
asn: String,
73-
colo: String,
70+
pub city: String,
71+
pub country: String,
72+
pub ip: String,
73+
pub asn: String,
74+
pub colo: String,
7475
}
7576

7677
impl Display for Metadata {
@@ -146,6 +147,7 @@ pub fn speed_test(client: Client, options: SpeedTestCLIOptions) -> Vec<Measureme
146147
payload_sizes,
147148
options.verbose,
148149
options.output_format,
150+
Some(&metadata),
149151
);
150152
measurements
151153
}

0 commit comments

Comments
 (0)