| // Copyright 2019 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 { |
| diagnostics_data::InspectData, |
| fuchsia_inspect::reader::{ArrayContent, DiagnosticsHierarchy, Property}, |
| nom::HexDisplay, |
| num_traits::Bounded, |
| std::{ |
| fmt, |
| ops::{Add, AddAssign, MulAssign}, |
| }, |
| }; |
| |
| const INDENT: usize = 2; |
| const HEX_DISPLAY_CHUNK_SIZE: usize = 16; |
| |
| pub fn format(path: &str, diagnostics_hierarchy: DiagnosticsHierarchy) -> String { |
| let result = output_hierarchy(diagnostics_hierarchy, 1); |
| format!("{}:\n{}", path, result) |
| } |
| |
| pub fn format_schema(schema: InspectData) -> String { |
| let mut result = format!("{}:\n", schema.moniker); |
| |
| result.push_str(" metadata:\n"); |
| if let Some(errors) = &schema.metadata.errors { |
| result.push_str(&format!(" errors = {}\n", errors.join(", "))); |
| } |
| result.push_str(&format!(" filename = {}\n", schema.metadata.filename)); |
| result.push_str(&format!(" component_url = {}\n", schema.metadata.component_url)); |
| result.push_str(&format!(" timestamp = {}\n", schema.metadata.timestamp)); |
| |
| match schema.payload { |
| Some(hierarchy) => { |
| let payload = output_hierarchy(hierarchy, 2); |
| result.push_str(&format!(" payload:\n{}", payload)) |
| } |
| None => result.push_str(" payload: null"), |
| } |
| result |
| } |
| |
| fn output_hierarchy(diagnostics_hierarchy: DiagnosticsHierarchy, indent: usize) -> String { |
| let mut lines = vec![]; |
| let name_indent = " ".repeat(INDENT * indent); |
| let value_indent = " ".repeat(INDENT * (indent + 1)); |
| |
| lines.push(format!("{}{}:", name_indent, diagnostics_hierarchy.name)); |
| |
| lines.extend(diagnostics_hierarchy.properties.into_iter().map(|property| match property { |
| Property::String(name, value) => format!("{}{} = {}", value_indent, name, value), |
| Property::Int(name, value) => format!("{}{} = {}", value_indent, name, value), |
| Property::Uint(name, value) => format!("{}{} = {}", value_indent, name, value), |
| Property::Double(name, value) => format!("{}{} = {:.6}", value_indent, name, value), |
| Property::Bytes(name, array) => { |
| let byte_str = array.to_hex(HEX_DISPLAY_CHUNK_SIZE); |
| format!("{}{} = Binary:\n{}", value_indent, name, byte_str.trim()) |
| } |
| Property::Bool(name, value) => format!("{}{} = {}", value_indent, name, value), |
| Property::IntArray(name, array) => output_array(&value_indent, &name, &array), |
| Property::UintArray(name, array) => output_array(&value_indent, &name, &array), |
| Property::DoubleArray(name, array) => output_array(&value_indent, &name, &array), |
| Property::StringList(name, list) => format!("{}{} = {:?}", value_indent, name, list), |
| })); |
| |
| lines.extend( |
| diagnostics_hierarchy.children.into_iter().map(|child| output_hierarchy(child, indent + 1)), |
| ); |
| |
| lines.join("\n") |
| } |
| |
| fn output_array< |
| T: AddAssign + MulAssign + Copy + Add<Output = T> + fmt::Display + NumberFormat + Bounded, |
| >( |
| value_indent: &str, |
| name: &str, |
| array: &ArrayContent<T>, |
| ) -> String { |
| let content = match array { |
| ArrayContent::Values(values) => { |
| values.iter().map(|x| x.to_string()).collect::<Vec<String>>() |
| } |
| ArrayContent::Buckets(buckets) => buckets |
| .iter() |
| .map(|bucket| { |
| format!( |
| "[{},{})={}", |
| bucket.floor.format(), |
| bucket.ceiling.format(), |
| bucket.count.format() |
| ) |
| }) |
| .collect::<Vec<String>>(), |
| }; |
| format!("{}{} = [{}]", value_indent, name, content.join(", ")) |
| } |
| |
| trait NumberFormat { |
| fn format(&self) -> String; |
| } |
| |
| impl NumberFormat for i64 { |
| fn format(&self) -> String { |
| match *self { |
| std::i64::MAX => "<max>".to_string(), |
| std::i64::MIN => "<min>".to_string(), |
| x => format!("{}", x), |
| } |
| } |
| } |
| |
| impl NumberFormat for u64 { |
| fn format(&self) -> String { |
| match *self { |
| std::u64::MAX => "<max>".to_string(), |
| x => format!("{}", x), |
| } |
| } |
| } |
| |
| impl NumberFormat for f64 { |
| fn format(&self) -> String { |
| if *self == std::f64::MAX || *self == std::f64::INFINITY { |
| "inf".to_string() |
| } else if *self == std::f64::MIN || *self == std::f64::NEG_INFINITY { |
| "-inf".to_string() |
| } else { |
| format!("{}", self) |
| } |
| } |
| } |