blob: 1357d0e2662531575895b59a0b18fd398a3487bc [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
fuchsia_inspect_node_hierarchy::{
serialization::{HierarchySerializer, RawJsonNodeHierarchySerializer},
NodeHierarchy,
},
fuchsia_zircon::Time,
lazy_static::lazy_static,
serde::{ser::SerializeSeq, Serialize, Serializer},
serde_json::Value,
};
lazy_static! {
static ref SCHEMA_VERSION: i64 = 1;
}
#[derive(Serialize, Debug, PartialEq, Eq)]
pub enum DataSource {
Inspect,
}
#[derive(Serialize, Debug, PartialEq, Eq)]
pub struct Error {
pub message: String,
}
fn serialize_time<S>(time: &Time, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_i64(time.into_nanos())
}
fn serialize_errors<S>(errors: &Option<Vec<Error>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match errors {
Some(errors) => {
let mut seq = serializer.serialize_seq(Some(errors.len()))?;
for element in errors {
seq.serialize_element(&element.message)?;
}
seq.end()
}
None => serializer.serialize_none(),
}
}
#[derive(Serialize, Debug)]
pub struct InspectMetadata {
/// Optional vector of errors encountered by platform.
#[serde(serialize_with = "serialize_errors")]
pub errors: Option<Vec<Error>>,
/// Name of diagnostics file producing data.
pub filename: String,
/// Monotonic time in nanos.
#[serde(serialize_with = "serialize_time")]
pub timestamp: Time,
}
#[derive(Serialize, Debug)]
pub struct JsonInspectSchema {
/// Enum specifying that this schema is encoding inspect data.
pub data_source: DataSource,
/// The inspect metadata for the diagnostics payload.
pub metadata: InspectMetadata,
/// Moniker of the component that generated the payload.
pub moniker: String,
/// Payload containing diagnostics data, if the payload exists, else None.
pub payload: Option<Value>,
/// Schema version.
pub version: i64,
}
impl JsonInspectSchema {
pub fn new(
moniker: String,
inspect_hierarchy: Option<NodeHierarchy>,
timestamp: Time,
filename: String,
errors: Vec<Error>,
) -> JsonInspectSchema {
let node_hierarchy_json_encoding_opt = inspect_hierarchy
.map(|inspect_hierarchy| RawJsonNodeHierarchySerializer::serialize(inspect_hierarchy));
let errors_opt = if errors.is_empty() { None } else { Some(errors) };
let inspect_metadata = InspectMetadata { timestamp, filename, errors: errors_opt };
JsonInspectSchema {
moniker,
version: *SCHEMA_VERSION,
data_source: DataSource::Inspect,
payload: node_hierarchy_json_encoding_opt,
metadata: inspect_metadata,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_inspect_node_hierarchy::Property;
use serde_json::json;
#[test]
fn test_canonicol_formatting() {
let mut hierarchy = NodeHierarchy::new(
"root",
vec![Property::String("x".to_string(), "foo".to_string())],
vec![],
);
hierarchy.sort();
let json_schema = JsonInspectSchema::new(
"a/b/c/d".to_string(),
Some(hierarchy),
Time::from_nanos(123456),
"test_file_plz_ignore.inspect".to_string(),
Vec::new(),
);
let pretty_json_string =
serde_json::to_string_pretty(&json_schema).expect("serialization should succeed.");
let expected_json = json!({
"moniker": "a/b/c/d",
"version": 1,
"data_source": "Inspect",
"payload": {
"root": {
"x": "foo"
}
},
"metadata": {
"timestamp": 123456,
"errors": null,
"filename": "test_file_plz_ignore.inspect"
}
});
let expect_json_pretty_string = serde_json::to_string_pretty(&expected_json)
.expect("serialization should be successful.");
assert_eq!(pretty_json_string, expect_json_pretty_string, "golden diff failed.");
}
#[test]
fn test_errorful_formatting() {
let json_schema = JsonInspectSchema::new(
"a/b/c/d".to_string(),
None,
Time::from_nanos(123456),
"test_file_plz_ignore.inspect".to_string(),
vec![Error { message: "too much fun being had.".to_string() }],
);
let pretty_json_string =
serde_json::to_string_pretty(&json_schema).expect("serialization should succeed.");
let expected_json = json!({
"moniker": "a/b/c/d",
"version": 1,
"data_source": "Inspect",
"payload": null,
"metadata": {
"timestamp": 123456,
"errors": ["too much fun being had."],
"filename": "test_file_plz_ignore.inspect"
}
});
let expect_json_pretty_string = serde_json::to_string_pretty(&expected_json)
.expect("serialization should be successful.");
assert_eq!(pretty_json_string, expect_json_pretty_string, "golden diff failed.");
}
}