fuchsia_inspect_derive/
lib.rs

1// Copyright 2020 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//! This crate provides types, traits and macros for ergonomic
6//! interactions with `fuchsia_inspect`. Proc macros are originally defined
7//! in a separate crate, but re-exported here. Users should depend directly
8//! on this crate.
9
10mod inspect;
11
12use core::fmt;
13use core::ops::{Deref, DerefMut};
14use fuchsia_inspect::{
15    BoolProperty, BytesProperty, DoubleProperty, IntProperty, Node, Property, StringProperty,
16    UintProperty,
17};
18pub use inspect::{AttachError, Inspect, WithInspect};
19use std::marker::PhantomData;
20
21/// Re-export Node, used by the procedural macros in order to get a canonical,
22/// stable import path. User code does not need `fuchsia_inspect` in their
23/// namespace.
24#[doc(hidden)]
25pub use fuchsia_inspect::Node as InspectNode;
26
27/// The `Unit` derive macro can be applied to named structs in order to generate an
28/// implementation of the `Unit` trait. The name of the field corresponds to the
29/// inspect node or property name, and the type of the field must also implement `Unit`.
30/// Implementations of `Unit` are supplied for most primitives and `String`.
31///
32/// Example:
33///
34/// #[derive(Unit)]
35/// struct Point {
36///     x: f32,
37///     y: f32,
38/// }
39pub use fuchsia_inspect_derive_macro::{Inspect, Unit};
40
41/// Provides a custom inspect `fuchsia_inspect` subtree for a type which is
42/// created, updated and removed in a single step. (It does NOT support per-field updates.)
43pub trait Unit {
44    /// This associated type owns a subtree (either a property or a node) of a parent inspect node.
45    /// May be nested. When dropped, the subtree is detached from the parent.
46    /// Default is required such that a detached state can be constructed. The base inspect node
47    /// and property types implement default.
48    type Data: Default;
49
50    /// Insert an inspect subtree at `parent[name]` with values from `self` and return
51    /// the inspect data.
52    fn inspect_create(&self, parent: &Node, name: impl AsRef<str>) -> Self::Data;
53
54    /// Update the inspect subtree owned by the inspect data with values from self.
55    fn inspect_update(&self, data: &mut Self::Data);
56}
57
58impl Unit for String {
59    type Data = StringProperty;
60
61    fn inspect_create(&self, parent: &Node, name: impl AsRef<str>) -> Self::Data {
62        parent.create_string(name.as_ref(), self)
63    }
64
65    fn inspect_update(&self, data: &mut Self::Data) {
66        data.set(self);
67    }
68}
69
70impl Unit for Vec<u8> {
71    type Data = BytesProperty;
72
73    fn inspect_create(&self, parent: &Node, name: impl AsRef<str>) -> Self::Data {
74        parent.create_bytes(name.as_ref(), self)
75    }
76
77    fn inspect_update(&self, data: &mut Self::Data) {
78        data.set(self);
79    }
80}
81
82/// Implement `Unit` for a primitive type. Some implementations result in a
83/// non-lossy upcast in order to conform to the supported types in the inspect API.
84///   `impl_t`: The primitive types to be implemented, e.g. `{ u8, u16 }`
85///   `inspect_t`: The type the inspect API expects, e.g. `u64`
86///   `prop_name`: The name the inspect API uses for functions, e.g. `uint`
87///   `prop_name_cap`: The name the inspect API uses for types, e.g. `Uint`
88macro_rules! impl_unit_primitive {
89    ({ $($impl_t:ty), *}, $inspect_t:ty, $prop_name:ident, $prop_name_cap:ident) => {
90        $(
91            paste::paste! {
92                impl Unit for $impl_t {
93                    type Data = [<$prop_name_cap Property>];
94
95                    fn inspect_create(&self, parent: &Node, name: impl AsRef<str>) -> Self::Data {
96                        parent.[<create_ $prop_name>](name.as_ref(), *self as $inspect_t)
97                    }
98
99                    fn inspect_update(&self, data: &mut Self::Data) {
100                        data.set(*self as $inspect_t);
101                    }
102                }
103            }
104        )*
105    };
106}
107
108// Implement `Unit` for the supported primitive types.
109impl_unit_primitive!({ u8, u16, u32, u64, usize }, u64, uint, Uint);
110impl_unit_primitive!({ i8, i16, i32, i64, isize }, i64, int, Int);
111impl_unit_primitive!({ f32, f64 }, f64, double, Double);
112impl_unit_primitive!({ bool }, bool, bool, Bool);
113
114/// The inspect data of an Option<T> gets the same inspect representation as T,
115/// but can also be absent.
116pub struct OptionData<T: Unit> {
117    // Keep a copy of the owned name, so that the inner node or property can be
118    // reinitialized after initial attachment.
119    name: String,
120
121    // Keep a reference to the parent, so that the inner node or property can be
122    // reinitialized after initial attachment.
123    inspect_parent: Node,
124
125    // Inner inspect data.
126    inspect_data: Option<T::Data>,
127}
128
129impl<T: Unit> Default for OptionData<T> {
130    fn default() -> Self {
131        Self { name: String::default(), inspect_parent: Node::default(), inspect_data: None }
132    }
133}
134
135impl<T: Unit> Unit for Option<T> {
136    type Data = OptionData<T>;
137
138    fn inspect_create(&self, parent: &Node, name: impl AsRef<str>) -> Self::Data {
139        Self::Data {
140            name: String::from(name.as_ref()),
141            inspect_parent: parent.clone_weak(),
142            inspect_data: self.as_ref().map(|inner| inner.inspect_create(parent, name.as_ref())),
143        }
144    }
145
146    fn inspect_update(&self, data: &mut Self::Data) {
147        match (self.as_ref(), &mut data.inspect_data) {
148            // None, always unset inspect data
149            (None, ref mut inspect_data) => **inspect_data = None,
150
151            // Missing inspect data, initialize it
152            (Some(inner), None) => {
153                data.inspect_data = Some(inner.inspect_create(&data.inspect_parent, &data.name));
154            }
155
156            // Update existing inspect data, for performance
157            (Some(inner), Some(ref mut inner_inspect_data)) => {
158                inner.inspect_update(inner_inspect_data);
159            }
160        }
161    }
162}
163
164/// Renders inspect state. This trait should be implemented with
165/// a relevant constraint on the base type.
166pub trait Render {
167    /// The base type, provided by the user.
168    type Base;
169
170    /// Inspect data, provided by implementors of this trait.
171    type Data: Default;
172
173    /// Initializes the inspect data from the current state of base.
174    fn create(base: &Self::Base, parent: &Node, name: impl AsRef<str>) -> Self::Data;
175
176    /// Updates the inspect data from the current state of base.
177    fn update(base: &Self::Base, data: &mut Self::Data);
178}
179
180/// Generic smart pointer which owns an inspect subtree (either a Node or a
181/// Property) for the duration of its lifetime. It dereferences to the
182/// user-provided base type (similar to Arc and other smart pointers).
183/// This type should rarely be used declared explictly. Rather, a specific smart
184/// pointer (such as IValue) should be used.
185pub struct IOwned<R: Render> {
186    _base: R::Base,
187    _inspect_data: R::Data,
188}
189
190impl<R: Render> IOwned<R> {
191    /// Construct the smart pointer but don't populate any inspect state.
192    pub fn new(value: R::Base) -> Self {
193        let _inspect_data = R::Data::default();
194        Self { _base: value, _inspect_data }
195    }
196
197    /// Construct the smart pointer and populate the inspect state under parent[name].
198    pub fn attached(value: R::Base, parent: &Node, name: impl AsRef<str>) -> Self {
199        let _inspect_data = R::create(&value, parent, name.as_ref());
200        Self { _base: value, _inspect_data }
201    }
202
203    /// Returns a RAII guard which can be used for mutations. When the guard
204    /// goes out of scope, the new inspect state is published.
205    pub fn as_mut(&mut self) -> IOwnedMutGuard<'_, R> {
206        IOwnedMutGuard(self)
207    }
208
209    /// Set the value, then update inspect state.
210    pub fn iset(&mut self, value: R::Base) {
211        self._base = value;
212        R::update(&self._base, &mut self._inspect_data);
213    }
214
215    pub fn into_inner(self) -> R::Base {
216        self._base
217    }
218}
219
220impl<R: Render> Inspect for &mut IOwned<R> {
221    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
222        self._inspect_data = R::create(&self._base, parent, name.as_ref());
223        Ok(())
224    }
225}
226
227impl<R, B> fmt::Debug for IOwned<R>
228where
229    R: Render<Base = B>,
230    B: fmt::Debug,
231{
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        fmt::Debug::fmt(&self._base, f)
234    }
235}
236
237impl<R, B> fmt::Display for IOwned<R>
238where
239    R: Render<Base = B>,
240    B: fmt::Display,
241{
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        fmt::Display::fmt(&self._base, f)
244    }
245}
246
247impl<R, B> Default for IOwned<R>
248where
249    R: Render<Base = B>,
250    B: Default,
251{
252    fn default() -> Self {
253        let _inspect_data = R::Data::default();
254        let _base = B::default();
255        Self { _base, _inspect_data }
256    }
257}
258
259impl<R: Render> Deref for IOwned<R> {
260    type Target = R::Base;
261    fn deref(&self) -> &Self::Target {
262        &self._base
263    }
264}
265
266/// A RAII implementation of a scoped guard of an IOwned smart pointer. When
267/// this structure is dropped (falls out of scope), the new inspect state will
268/// be published.
269pub struct IOwnedMutGuard<'a, R: Render>(&'a mut IOwned<R>);
270
271impl<R: Render> Deref for IOwnedMutGuard<'_, R> {
272    type Target = R::Base;
273    fn deref(&self) -> &R::Base {
274        &self.0._base
275    }
276}
277
278impl<R: Render> DerefMut for IOwnedMutGuard<'_, R> {
279    fn deref_mut(&mut self) -> &mut R::Base {
280        &mut self.0._base
281    }
282}
283
284impl<R: Render> Drop for IOwnedMutGuard<'_, R> {
285    fn drop(&mut self) {
286        R::update(&self.0._base, &mut self.0._inspect_data);
287    }
288}
289
290#[doc(hidden)]
291pub struct ValueMarker<B: Unit>(PhantomData<B>);
292
293impl<B: Unit> Render for ValueMarker<B> {
294    type Base = B;
295    type Data = B::Data;
296
297    fn create(base: &Self::Base, parent: &Node, name: impl AsRef<str>) -> Self::Data {
298        base.inspect_create(parent, name.as_ref())
299    }
300
301    fn update(base: &Self::Base, data: &mut Self::Data) {
302        base.inspect_update(data);
303    }
304}
305
306/// An `Inspect` smart pointer for a type `B`, which renders an
307/// inspect subtree as defined by `B: Unit`.
308pub type IValue<B> = IOwned<ValueMarker<B>>;
309
310impl<B: Unit> From<B> for IValue<B> {
311    fn from(value: B) -> Self {
312        Self::new(value)
313    }
314}
315
316#[doc(hidden)]
317pub struct DebugMarker<B: fmt::Debug>(PhantomData<B>);
318
319impl<B: fmt::Debug> Render for DebugMarker<B> {
320    type Base = B;
321    type Data = StringProperty;
322
323    fn create(base: &Self::Base, parent: &Node, name: impl AsRef<str>) -> Self::Data {
324        parent.create_string(name.as_ref(), format!("{:?}", base))
325    }
326
327    fn update(base: &Self::Base, data: &mut Self::Data) {
328        data.set(&format!("{:?}", base));
329    }
330}
331
332/// An `Inspect` smart pointer for a type `B`, which renders the debug
333/// output of `B` as a string property.
334pub type IDebug<B> = IOwned<DebugMarker<B>>;
335
336impl<B: fmt::Debug> From<B> for IDebug<B> {
337    fn from(value: B) -> Self {
338        Self::new(value)
339    }
340}