Skip to main content

diagnostics_traits/
lib.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
5//! Target-agnostic abstractions for Inspect so that code that builds for both
6//! host and Fuchsia targets can implement Inspect support.
7
8#![no_std]
9extern crate alloc;
10
11#[cfg(target_os = "fuchsia")]
12mod fuchsia;
13
14#[cfg(target_os = "fuchsia")]
15pub use fuchsia::*;
16
17use alloc::format;
18use alloc::string::String;
19use alloc::sync::Arc;
20use core::fmt::{Debug, Display};
21
22use net_types::ip::IpAddress;
23
24/// A trait abstracting a state inspector.
25///
26/// This trait follows roughly the same shape as the API provided by the
27/// fuchsia_inspect crate, but we abstract it out so not to take the dependency.
28///
29/// Given we have the trait, we can fill it in with some helpful default
30/// implementations for common types that are exposed as well, like IP addresses
31/// and such.
32pub trait Inspector: Sized {
33    /// The type given to record contained children.
34    type ChildInspector<'a>: Inspector;
35
36    /// Records a nested inspector with `name` calling `f` with the nested child
37    /// to be filled in.
38    ///
39    /// This is used to group and contextualize data.
40    fn record_child<F: FnOnce(&mut Self::ChildInspector<'_>)>(&mut self, name: &str, f: F);
41
42    /// Records a child without a name.
43    ///
44    /// The `Inpector` is expected to keep track of the number of unnamed
45    /// children and allocate names appropriately from that.
46    fn record_unnamed_child<F: FnOnce(&mut Self::ChildInspector<'_>)>(&mut self, f: F);
47
48    /// Records a child whose name is the display implementation of `T`.
49    fn record_display_child<T: Display, F: FnOnce(&mut Self::ChildInspector<'_>)>(
50        &mut self,
51        name: T,
52        f: F,
53    ) {
54        self.record_child(&format!("{name}"), f)
55    }
56
57    /// Records a child whose name is the Debug implementation of `T`.
58    fn record_debug_child<T: Debug, F: FnOnce(&mut Self::ChildInspector<'_>)>(
59        &mut self,
60        name: T,
61        f: F,
62    ) {
63        self.record_child(&format!("{name:?}"), f)
64    }
65
66    /// Records anything that can be represented by a usize.
67    fn record_usize<T: Into<usize>>(&mut self, name: &str, value: T);
68
69    /// Records anything that can be represented by a u64.
70    fn record_uint<T: Into<u64>>(&mut self, name: &str, value: T);
71
72    /// Records anything that can be represented by a i64.
73    fn record_int<T: Into<i64>>(&mut self, name: &str, value: T);
74
75    /// Records anything that can be represented by a f64.
76    fn record_double<T: Into<f64>>(&mut self, name: &str, value: T);
77
78    /// Records a str value.
79    fn record_str(&mut self, name: &str, value: &str);
80
81    /// Records an owned string.
82    fn record_string(&mut self, name: &str, value: String);
83
84    /// Records a boolean.
85    fn record_bool(&mut self, name: &str, value: bool);
86
87    /// Records a `value` that implements `Display` as its display string.
88    fn record_display<T: Display>(&mut self, name: &str, value: T) {
89        self.record_string(name, format!("{value}"))
90    }
91
92    /// Records a `value` that implements `Debug` as its debug string.
93    fn record_debug<T: Debug>(&mut self, name: &str, value: T) {
94        self.record_string(name, format!("{value:?}"))
95    }
96
97    /// Records an IP address.
98    fn record_ip_addr<A: IpAddress>(&mut self, name: &str, value: A) {
99        self.record_display(name, value)
100    }
101
102    /// Records an implementor of [`InspectableValue`].
103    fn record_inspectable_value<V: InspectableValue>(&mut self, name: &str, value: &V) {
104        value.record(name, self)
105    }
106
107    /// Records an implementor of [`InspectableInstant`].
108    fn record_instant<V: InspectableInstant>(&mut self, name: InstantPropertyName, value: &V) {
109        value.record(name, self)
110    }
111
112    /// Records an implementor of [`Inspectable`] under `name`.
113    fn record_inspectable<V: Inspectable>(&mut self, name: &str, value: &V) {
114        self.record_child(name, |inspector| {
115            inspector.delegate_inspectable(value);
116        });
117    }
118
119    /// Delegates more fields to be added by an [`Inspectable`] implementation.
120    fn delegate_inspectable<V: Inspectable>(&mut self, value: &V) {
121        value.record(self)
122    }
123}
124
125/// A trait that allows a type to record its fields to an `inspector`.
126///
127/// This trait is used for types that are exposed to [`Inspector`]s many times
128/// so recording them can be deduplicated.
129pub trait Inspectable {
130    /// Records this value into `inspector`.
131    fn record<I: Inspector>(&self, inspector: &mut I);
132}
133
134impl Inspectable for () {
135    fn record<I: Inspector>(&self, _inspector: &mut I) {}
136}
137
138/// A trait that marks a type as inspectable.
139///
140/// This trait is used for types that are exposed to [`Inspector`]s many times
141/// so recording them can be deduplicated.
142///
143/// This type differs from [`Inspectable`] in that it receives a `name`
144/// parameter. This is typically used for types that record a single entry.
145pub trait InspectableValue {
146    /// Records this value into `inspector`.
147    fn record<I: Inspector>(&self, name: &str, inspector: &mut I);
148}
149
150impl InspectableValue for ! {
151    fn record<I: Inspector>(&self, _name: &str, _inspector: &mut I) {
152        match *self {}
153    }
154}
155
156impl<V: InspectableValue> InspectableValue for Arc<V> {
157    fn record<I: Inspector>(&self, name: &str, inspector: &mut I) {
158        self.as_ref().record(name, inspector)
159    }
160}
161
162/// An extension to `Inspector` that allows transforming and recording device
163/// identifiers.
164///
165/// How to record device IDs is delegated to bindings via this trait, so we
166/// don't need to propagate `InspectableValue` implementations everywhere in
167/// core unnecessarily.
168pub trait InspectorDeviceExt<D> {
169    /// Records an entry named `name` with value `device`.
170    fn record_device<I: Inspector>(inspector: &mut I, name: &str, device: &D);
171
172    /// Returns the `Display` representation of the IPv6 scoped address zone
173    /// associated with `D`.
174    fn device_identifier_as_address_zone(device: D) -> impl Display;
175}
176
177/// An extension to `Inspector` that allows recoding route table IDs.
178pub trait InspectorRouteTableExt<R> {
179    /// Records an entry named `name` with value `table`.
180    fn record_route_table<I: Inspector>(inspector: &mut I, name: &str, table: &R);
181    /// Returns the `Display` representation of `table`.
182    fn display_route_table(table: &R) -> impl Display;
183}
184
185/// A trait that marks a type as an inspectable representation of an instant in
186/// time.
187pub trait InspectableInstant {
188    /// Records this value into `inspector`.
189    fn record<I: Inspector>(&self, name: InstantPropertyName, inspector: &mut I);
190}
191
192/// A name suitable for use for recording an Instant property representing a
193/// moment in time.
194///
195/// This type exists because Fuchsia Snapshot Viewer has special treatment of
196/// property names ending in the magic string "@time".
197/// [`crate::instant_property_name`] should be used to construct this type and
198/// ensure that the "@time" suffix is added.
199#[derive(Copy, Clone)]
200pub struct InstantPropertyName {
201    inner: &'static str,
202}
203
204impl InstantPropertyName {
205    pub fn get(&self) -> &'static str {
206        self.inner
207    }
208}
209
210impl From<InstantPropertyName> for &'static str {
211    fn from(InstantPropertyName { inner }: InstantPropertyName) -> Self {
212        inner
213    }
214}
215
216/// Implementation details that need to be `pub` in order to be used from macros
217/// but should not be used otherwise.
218#[doc(hidden)]
219pub mod internal {
220    use super::InstantPropertyName;
221
222    /// Constructs an [`InstantPropertyName`].
223    ///
224    /// Use [`crate::instant_property_name`] instead.
225    pub fn construct_instant_property_name_do_not_use(inner: &'static str) -> InstantPropertyName {
226        InstantPropertyName { inner }
227    }
228}
229
230/// Constructs an [`InstantPropertyName`] to use while recording Instants.
231#[macro_export]
232macro_rules! instant_property_name {
233    () => {
234        $crate::internal::construct_instant_property_name_do_not_use("@time")
235    };
236    ($lit:literal) => {
237        $crate::internal::construct_instant_property_name_do_not_use(core::concat!($lit, "@time"))
238    };
239}