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