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