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}