diagnostics_traits/
fuchsia.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use alloc::format;
6use alloc::string::String;
7use core::fmt::Display;
8use core::marker::PhantomData;
9
10use fuchsia_async as fasync;
11use fuchsia_inspect::Node;
12use log::warn;
13
14use crate::{
15    InspectableInstant, Inspector, InspectorDeviceExt, InspectorRouteTableExt, InstantPropertyName,
16};
17
18/// Provides an abstract interface for extracting inspect device identifier.
19pub trait InspectorDeviceIdProvider<DeviceId> {
20    /// Extracts the device identifier from the provided opaque type.
21    fn device_id(id: &DeviceId) -> u64;
22}
23
24/// Provides an abstract interface for extracting inspect route table identifier.
25pub trait InspectorRouteTableIdProvider<R> {
26    /// Extracts the route table identifier from the provided opaque type.
27    fn route_table_id(id: &R) -> u32;
28}
29
30/// Provides a Fuchsia implementation of `Inspector`.
31pub struct FuchsiaInspector<'a, D> {
32    node: &'a Node,
33    unnamed_count: usize,
34    _marker: PhantomData<D>,
35}
36
37impl<'a, D> FuchsiaInspector<'a, D> {
38    /// Create a new `FuchsiaInspector` rooted at `node`.
39    pub fn new(node: &'a Node) -> Self {
40        Self { node, unnamed_count: 0, _marker: Default::default() }
41    }
42}
43
44impl<'a, D> Inspector for FuchsiaInspector<'a, D> {
45    type ChildInspector<'l> = FuchsiaInspector<'l, D>;
46
47    fn record_child<F: FnOnce(&mut Self::ChildInspector<'_>)>(&mut self, name: &str, f: F) {
48        self.node.record_child(name, |node| f(&mut FuchsiaInspector::new(node)))
49    }
50
51    fn record_unnamed_child<F: FnOnce(&mut Self::ChildInspector<'_>)>(&mut self, f: F) {
52        let Self { node: _, unnamed_count, _marker: _ } = self;
53        let id = core::mem::replace(unnamed_count, *unnamed_count + 1);
54        self.record_child(&format!("{id}"), f)
55    }
56
57    fn record_usize<T: Into<usize>>(&mut self, name: &str, value: T) {
58        let value: u64 = value.into().try_into().unwrap_or_else(|e| {
59            warn!("failed to inspect usize value that does not fit in a u64: {e:?}");
60            u64::MAX
61        });
62        self.node.record_uint(name, value)
63    }
64
65    fn record_uint<T: Into<u64>>(&mut self, name: &str, value: T) {
66        self.node.record_uint(name, value.into())
67    }
68
69    fn record_int<T: Into<i64>>(&mut self, name: &str, value: T) {
70        self.node.record_int(name, value.into())
71    }
72
73    fn record_double<T: Into<f64>>(&mut self, name: &str, value: T) {
74        self.node.record_double(name, value.into())
75    }
76
77    fn record_str(&mut self, name: &str, value: &str) {
78        self.node.record_string(name, value)
79    }
80
81    fn record_string(&mut self, name: &str, value: String) {
82        self.node.record_string(name, value)
83    }
84
85    fn record_bool(&mut self, name: &str, value: bool) {
86        self.node.record_bool(name, value)
87    }
88}
89
90impl<'a, D, P: InspectorDeviceIdProvider<D>> InspectorDeviceExt<D> for FuchsiaInspector<'a, P> {
91    fn record_device<I: Inspector>(inspector: &mut I, name: &str, device: &D) {
92        inspector.record_uint(name, P::device_id(device))
93    }
94
95    fn device_identifier_as_address_zone(id: D) -> impl Display {
96        P::device_id(&id)
97    }
98}
99
100impl<'a, R, P: InspectorRouteTableIdProvider<R>> InspectorRouteTableExt<R>
101    for FuchsiaInspector<'a, P>
102{
103    fn record_route_table<I: Inspector>(inspector: &mut I, name: &str, table: &R) {
104        inspector.record_uint(name, P::route_table_id(table))
105    }
106
107    fn display_route_table(table: &R) -> impl Display {
108        P::route_table_id(table)
109    }
110}
111
112impl InspectableInstant for fasync::MonotonicInstant {
113    fn record<I: Inspector>(&self, name: InstantPropertyName, inspector: &mut I) {
114        inspector.record_int(name.into(), self.into_nanos());
115    }
116}