mod impls;
mod wrappers;
pub use fuchsia_inspect::StringReference;
pub use wrappers::{InspectBytes, InspectList, InspectListClosure, InspectUintArray};
use fuchsia_inspect::Node;
pub trait WriteInspect {
fn write_inspect(&self, writer: &Node, key: impl Into<StringReference>);
}
#[macro_export]
macro_rules! inspect_log {
($bounded_list_node:expr, $($args:tt)+) => {{
use $crate::{inspect_insert, nodes::{NodeTimeExt, BootTimeline}};
$bounded_list_node.add_entry(|node| {
NodeTimeExt::<BootTimeline>::record_time(node, "@time");
inspect_insert!(@internal_inspect_log node, $($args)+);
});
}};
}
#[macro_export]
macro_rules! inspect_insert {
(@internal $node_writer:expr,) => {};
(@internal $node_writer:expr, var $key:ident: { $($sub:tt)+ }) => {{
let child_writer = $node_writer.create_child($key);
inspect_insert!(@internal child_writer, $($sub)+);
$node_writer.record(child_writer);
}};
(@internal $node_writer:expr, var $key:ident: { $($sub:tt)+ }, $($rest:tt)*) => {{
inspect_insert!(@internal $node_writer, var $key: { $($sub)+ });
inspect_insert!(@internal $node_writer, $($rest)*);
}};
(@internal $node_writer:expr, var $key:ident: $val:expr) => {{
$val.write_inspect(&$node_writer, $key);
}};
(@internal $node_writer:expr, var $key:ident: $val:expr, $($rest:tt)*) => {{
inspect_insert!(@internal $node_writer, var $key: $val);
inspect_insert!(@internal $node_writer, $($rest)*);
}};
(@internal $node_writer:expr, var $key:ident?: $val:expr) => {{
match $val {
Some(val) => inspect_insert!(@internal $node_writer, var $key: val),
None => (),
}
}};
(@internal $node_writer:expr, var $key:ident?: $val:expr, $($rest:tt)*) => {{
inspect_insert!(@internal $node_writer, var $key?: $val);
inspect_insert!(@internal $node_writer, $($rest)*);
}};
(@internal $node_writer:expr, $key:ident: $($rest:tt)+) => {{
let key = stringify!($key);
inspect_insert!(@internal $node_writer, var key: $($rest)+);
}};
(@internal $node_writer:expr, $key:ident?: $($rest:tt)+) => {{
let key = stringify!($key);
inspect_insert!(@internal $node_writer, var key?: $($rest)+);
}};
(@internal $node_writer:expr, $key:expr => $($rest:tt)+) => {{
let key: $crate::log::StringReference = $key.into();
inspect_insert!(@internal $node_writer, var key: $($rest)+);
}};
(@internal_inspect_log $node_writer:expr, { $($args:tt)* }) => {{
#[allow(unused_imports)]
use $crate::log::WriteInspect;
inspect_insert!(@internal $node_writer, $($args)*);
}};
(@internal_inspect_log $node_writer:expr, $($args:tt)+) => {{
use $crate::log::WriteInspect;
inspect_insert!(@internal $node_writer, $($args)+);
}};
($node_writer:expr, { $($args:tt)+ }) => {{
use $crate::log::WriteInspect;
inspect_insert!(@internal $node_writer, $($args)+);
}};
($node_writer:expr, $($args:tt)+) => {{
use $crate::log::WriteInspect;
inspect_insert!(@internal $node_writer, $($args)+);
}};
}
#[macro_export]
macro_rules! make_inspect_loggable {
($($args:tt)+) => {{
use $crate::inspect_insert;
use fuchsia_inspect::{Node, StringReference};
struct WriteInspectClosure<F>(F);
impl<F> WriteInspect for WriteInspectClosure<F> where F: Fn(&Node, StringReference) {
fn write_inspect(&self, writer: &Node, key: impl Into<StringReference>) {
self.0(writer, key.into());
}
}
let f = WriteInspectClosure(move |writer: &Node, key: StringReference| {
let child = writer.create_child(key);
inspect_insert!(child, $($args)+);
writer.record(child);
});
f
}};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nodes::BoundedListNode;
use diagnostics_assertions::{assert_data_tree, AnyProperty};
use fuchsia_inspect::{DiagnosticsHierarchyGetter, Inspector};
use fuchsia_sync::Mutex;
use test_util::assert_lt;
#[fuchsia::test]
fn test_inspect_log_basic() {
let (inspector, mut node) = inspector_and_list_node();
inspect_log!(node, k1: "1".to_string(), meaning_of_life: 42u64, k3: 3i64, k4: 4f64);
inspect_log!(node, small_uint: 1u8, small_int: 2i8, float: 3f32);
inspect_log!(node, {
s: "str",
uint: &13u8,
});
inspect_log!(node, {});
let hierarchy = inspector.get_diagnostics_hierarchy();
assert_data_tree!(hierarchy, root: {
list_node: {
"0": { "@time": AnyProperty, k1: "1", meaning_of_life: 42u64, k3: 3i64, k4: 4f64 },
"1": { "@time": AnyProperty, small_uint: 1u64, small_int: 2i64, float: 3f64 },
"2": { "@time": AnyProperty, s: "str", uint: 13u64 },
"3": { "@time": AnyProperty },
}
});
let get_time = |index| {
hierarchy
.get_property_by_path(&["list_node", index, "@time"])
.and_then(|p| p.int())
.unwrap()
};
assert_lt!(get_time("0"), get_time("1"));
assert_lt!(get_time("1"), get_time("2"));
assert_lt!(get_time("2"), get_time("3"));
}
#[fuchsia::test]
fn test_inspect_log_nested() {
let (inspector, mut node) = inspector_and_list_node();
inspect_log!(node, {
k1: {
sub1: "subval1",
sub2: {
subsub1: "subsubval1",
},
sub3: 3u64,
},
k2: if true { 10u64 } else { 20 }
});
assert_data_tree!(inspector, root: {
list_node: {
"0": {
"@time": AnyProperty,
k1: {
sub1: "subval1",
sub2: {
subsub1: "subsubval1",
},
sub3: 3u64,
},
k2: 10u64
}
}
});
}
#[fuchsia::test]
fn test_inspect_log_var_key_syntax() {
let (inspector, mut node) = inspector_and_list_node();
let key = "@@@";
inspect_log!(node, var key: "!!!");
assert_data_tree!(inspector, root: {
list_node: {
"0": {
"@time": AnyProperty,
"@@@": "!!!"
}
}
});
}
#[fuchsia::test]
fn test_inspect_log_parsing() {
let (_inspector, mut node) = inspector_and_list_node();
inspect_log!(node, k1: "v1", k2: "v2");
inspect_log!(node, k1: "v1", k2: "v2",);
inspect_log!(node, {
k1: "v1",
k2: "v2"
});
inspect_log!(node, {
k1: "v1",
k2: "v2",
});
}
#[fuchsia::test]
fn test_inspect_log_allows_mutex_guard_temporary() {
let (_inspector, node) = inspector_and_list_node();
let node = Mutex::new(node);
inspect_log!(node.lock(), k1: "v1");
}
#[fuchsia::test]
fn test_inspect_log_macro_does_not_move_value() {
let (_inspector, mut node) = inspector_and_list_node();
let s = String::from("s");
inspect_log!(node, s: s);
println!("{}", s);
}
#[fuchsia::test]
fn test_log_option() {
let (inspector, mut node) = inspector_and_list_node();
inspect_log!(node, some?: Some("a"));
inspect_log!(node, none?: None as Option<String>);
assert_data_tree!(inspector, root: {
list_node: {
"0": { "@time": AnyProperty, some: "a" },
"1": { "@time": AnyProperty },
}
});
}
#[fuchsia::test]
fn test_log_inspect_bytes() {
let (inspector, mut node) = inspector_and_list_node();
let bytes = [11u8, 22, 33];
inspect_log!(node, bytes: InspectBytes(&bytes));
inspect_log!(node, bytes: InspectBytes(&bytes[..]));
inspect_log!(node, bytes: InspectBytes(bytes));
assert_data_tree!(inspector, root: {
list_node: {
"0": { "@time": AnyProperty, bytes: vec![11u8, 22, 33] },
"1": { "@time": AnyProperty, bytes: vec![11u8, 22, 33] },
"2": { "@time": AnyProperty, bytes: vec![11u8, 22, 33] },
}
});
}
#[fuchsia::test]
fn test_log_inspect_list() {
let (inspector, mut node) = inspector_and_list_node();
let list = [11u8, 22, 33];
inspect_log!(node, list: InspectList(&list));
assert_data_tree!(inspector, root: {
list_node: {
"0": {
"@time": AnyProperty,
list: {
"0": 11u64,
"1": 22u64,
"2": 33u64,
}
}
}
});
}
#[fuchsia::test]
fn test_log_inspect_list_closure() {
let (inspector, mut node) = inspector_and_list_node();
let list = [13u32, 17, 29];
let list_mapped = InspectListClosure(&list, |node_writer, key, item| {
inspect_insert!(node_writer, var key: item * 2);
});
inspect_log!(node, list: list_mapped);
assert_data_tree!(inspector, root: {
list_node: {
"0": {
"@time": AnyProperty,
list: {
"0": 26u64,
"1": 34u64,
"2": 58u64,
}
}
}
});
}
#[fuchsia::test]
fn test_log_inspect_uint_array() {
let (inspector, mut node) = inspector_and_list_node();
let list = [1u32, 2, 3, 4, 5, 6];
inspect_log!(node, list: InspectUintArray::new(&list));
assert_data_tree!(inspector, root: {
list_node: {
"0": {
"@time": AnyProperty,
list: vec![1u64, 2, 3, 4, 5, 6],
}
}
});
}
#[fuchsia::test]
fn test_inspect_insert_parsing() {
let (_inspector, mut node) = inspector_and_list_node();
let node_writer = node.add_entry(|node_writer| {
inspect_insert!(node_writer, k1: "v1".to_string(), k2: if true { 10u64 } else { 20 });
});
inspect_insert!(node_writer, k1: 1i64, k2: 2f64,);
inspect_insert!(node_writer, {
k1: 1u8,
k2: 2i8
});
inspect_insert!(node_writer, {
k1: &1u64,
k2?: Some("v2"),
});
}
#[fuchsia::test]
fn test_make_inspect_loggable() {
let (inspector, mut node) = inspector_and_list_node();
let obj = make_inspect_loggable!(k1: "1", k2: 2i64, k3: "3");
inspect_log!(node, some_key: obj);
let point = Some((10i64, 50i64));
inspect_log!(node, point?: point.map(|(x, y)| make_inspect_loggable!({
x: x,
y: y,
})));
assert_data_tree!(inspector, root: {
list_node: {
"0": {
"@time": AnyProperty,
some_key: { k1: "1", k2: 2i64, k3: "3" },
},
"1": {
"@time": AnyProperty,
point: { x: 10i64, y: 50i64 },
},
}
});
}
#[fuchsia::test]
fn test_log_inspect_string_reference() {
let (inspector, mut node) = inspector_and_list_node();
inspect_log!(node, "foo" => "foo_1");
inspect_log!(node, "foo" => "foo_2");
inspect_log!(node, "foo" => "foo_3");
inspect_log!(node, "foo" => "foo_4");
assert_data_tree!(inspector, root: {
list_node: {
"0": { "@time": AnyProperty, foo: "foo_1" },
"1": { "@time": AnyProperty, foo: "foo_2" },
"2": { "@time": AnyProperty, foo: "foo_3" },
"3": { "@time": AnyProperty, foo: "foo_4" },
}
});
}
fn inspector_and_list_node() -> (Inspector, BoundedListNode) {
let inspector = Inspector::default();
let list_node = inspector.root().create_child("list_node");
let list_node = BoundedListNode::new(list_node, 10);
(inspector, list_node)
}
}