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