fuchsia_inspect_derive/
inspect.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
5use fuchsia_inspect::{
6    BoolProperty, BytesProperty, DoubleProperty, IntProperty, Node, Property, StringProperty,
7    UintProperty,
8};
9use futures::lock;
10use std::{cell, sync};
11use thiserror::Error;
12use tracing_mutex::lockapi::TracingWrapper;
13
14/// AttachError denotes a broken data acquisition invariant, such as when a
15/// mutex is held during attachment.
16#[derive(Error, Debug)]
17#[error("could not attach to inspect: {:?}", .msg)]
18pub struct AttachError {
19    msg: String,
20}
21
22impl From<&str> for AttachError {
23    fn from(msg: &str) -> Self {
24        Self { msg: msg.to_string() }
25    }
26}
27
28/// A trait for types that can be inspected using fuchsia_inspect and that
29/// maintain their inspect state during their lifetime. Notably, this trait is
30/// implemented for most fuchsia_inspect properties and the `IOwned` smart
31/// pointers. Generic implementations are also provided for interior mutability
32/// wrappers whose inner type also implement `Inspect`, such as RefCell and
33/// various Mutex types. This method should generally be implemented for a
34/// mutable reference (or a regular reference if the type has interior
35/// mutability).
36// TODO(https://fxbug.dev/42127490): Add a derive-macro to auto generate this trait.
37pub trait Inspect {
38    /// Attaches `self` to the inspect tree, under `parent[name]`. Note that:
39    ///
40    /// - If this method is called on a type with interior mutability, it
41    ///   will attempt to get exclusive access to the inner data, but will
42    ///   not wait for a mutex to unlock. If it fails, an `AttachError` is
43    ///   returned.
44    /// - If this method is called on a fuchsia_inspect Property, it is
45    ///   reinitialized to its default value.
46    ///
47    /// Therefore it is recommended to attach to inspect immediately after
48    /// initialization, although not within constructors. Whether or not
49    /// to use inspect should usually be a choice of the caller.
50    ///
51    /// NOTE: Implementors should avoid returning AttachError whenever
52    /// possible. It is reserved for irrecoverable invariant violations
53    /// (see above). Invalid data structure invariants are not attachment
54    /// errors and should instead be ignored and optionally logged.
55    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError>;
56}
57
58/// Implement `Inspect` for a fuchsia_inspect property.
59macro_rules! impl_inspect_property {
60    ($prop_name:ident, $prop_name_cap:ident) => {
61        paste::paste! {
62            impl Inspect for &mut [<$prop_name_cap Property>] {
63                fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
64                    let default = <[<$prop_name_cap Property>] as Property<'_>>::Type::default();
65                    *self = parent.[<create_ $prop_name>](name.as_ref(), default);
66                    Ok(())
67                }
68            }
69        }
70    };
71}
72
73impl_inspect_property!(uint, Uint);
74impl_inspect_property!(int, Int);
75impl_inspect_property!(double, Double);
76impl_inspect_property!(bool, Bool);
77impl_inspect_property!(string, String);
78impl_inspect_property!(bytes, Bytes);
79
80// Implement `Inspect` for interior mutability wrapper types.
81
82impl<T> Inspect for &cell::RefCell<T>
83where
84    for<'a> &'a mut T: Inspect,
85{
86    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
87        match self.try_borrow_mut() {
88            Ok(mut inner) => inner.iattach(parent, name),
89            Err(_) => Err("could not get exclusive access to cell::RefCell".into()),
90        }
91    }
92}
93
94impl<T> Inspect for &sync::Mutex<T>
95where
96    for<'a> &'a mut T: Inspect,
97{
98    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
99        match self.try_lock() {
100            Ok(mut inner) => inner.iattach(parent, name),
101            Err(_) => Err("could not get exclusive access to std::sync::Mutex".into()),
102        }
103    }
104}
105
106impl<T> Inspect for &sync::RwLock<T>
107where
108    for<'a> &'a mut T: Inspect,
109{
110    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
111        match self.try_write() {
112            Ok(mut inner) => inner.iattach(parent, name),
113            Err(_) => Err("could not get exclusive access to std::sync::RwLock".into()),
114        }
115    }
116}
117
118impl<T> Inspect for &fuchsia_sync::Mutex<T>
119where
120    for<'a> &'a mut T: Inspect,
121{
122    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
123        match self.try_lock() {
124            Some(mut inner) => inner.iattach(parent, name),
125            None => Err("could not get exclusive access to fuchsia_sync::Mutex".into()),
126        }
127    }
128}
129
130impl<T> Inspect for &fuchsia_sync::RwLock<T>
131where
132    for<'a> &'a mut T: Inspect,
133{
134    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
135        match self.try_write() {
136            Some(mut inner) => inner.iattach(parent, name),
137            None => Err("could not get exclusive access to fuchsia_sync::RwLock".into()),
138        }
139    }
140}
141
142impl<T> Inspect for &lock::Mutex<T>
143where
144    for<'a> &'a mut T: Inspect,
145{
146    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
147        match self.try_lock() {
148            Some(mut inner) => inner.iattach(parent, name),
149            None => Err("could not get exclusive access to futures::lock::Mutex".into()),
150        }
151    }
152}
153
154// Underlying type for `lock_api::Mutex`.
155impl<T> Inspect for &lock_api::Mutex<TracingWrapper<fuchsia_sync::RawSyncMutex>, T>
156where
157    for<'a> &'a mut T: Inspect,
158{
159    fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
160        match self.try_lock() {
161            Some(mut inner) => inner.iattach(parent, name),
162            None => Err("could not get exclusive access to lock_api::Mutex".into()),
163        }
164    }
165}
166
167/// Extension trait for types that #[derive(Inspect)] (or implements
168/// `Inspect for &mut T` some other way), providing a convenient way of
169/// attaching to inspect during construction. See the `Inspect` trait for
170/// more details.
171pub trait WithInspect
172where
173    Self: Sized,
174{
175    /// Attaches self to the inspect tree. It is recommended to invoke this as
176    /// part of construction. For example:
177    ///
178    /// `let yak =  Yak::new().with_inspect(inspector.root(), "my_yak")?;`
179    fn with_inspect(self, parent: &Node, name: impl AsRef<str>) -> Result<Self, AttachError>;
180}
181
182impl<T> WithInspect for T
183where
184    for<'a> &'a mut T: Inspect,
185{
186    fn with_inspect(mut self, parent: &Node, name: impl AsRef<str>) -> Result<Self, AttachError> {
187        self.iattach(parent, name).map(|()| self)
188    }
189}