fuchsia_inspect_contrib/inspectable/
mod.rs

1// Copyright 2019 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//! Provides utilities on top of regular inspect properties to enable convenient features such as:
6//!
7//! - Directly mutating a value and dumping it to inspect
8//!
9//!   ```rust
10//!   use fuchsia_inspect::component;
11//!   use fuchsia_inspect::contrib::inspectable::InspectableU64;
12//!
13//!   let inspectable = InspectableU64::new(0, component::inspector::root(), "foo");
14//!   *inspectable.get_mut() += 1;
15//!   // The value of the u64 is 1 now and it was updated automatically in the inspect vmo.
16//!   ```
17//!
18//! - Dump lengths automatically
19//!
20//!   ```rust
21//!   use fuchsia_inspect::component;
22//!   use fuchsia_inspect::contrib::inspectable::InspectableLen;
23//!
24//!   let inspectable = InspectableLen::new(vec![0], component::inspector::root(), "foo");
25//!   // The value of the vector is [0] and the value in the inspect vmo under the foo property
26//!   // is 1.
27//!   *inspectable.get_mut().push(3);
28//!   // The value of the vector is [0, 3] and the value in the inspect vmo under the foo property
29//!   // is 2.
30//!   ```
31//!
32//! - Dump debug representations automatically, etc.
33//!
34//!   ```rust
35//!   use fuchsia_inspect::component;
36//!   use fuchsia_inspect::contrib::inspectable::InspectableDebugString;
37//!
38//!   let inspectable = InspectableDebugString::new(vec![0], component::inspector::root(), "foo");
39//!   // The value of the vector is vec![0] and the value in the inspect vmo under the foo property
40//!   // is the debug string of the vector "[0]".
41//!   *inspectable.get_mut().push(3);
42//!   // The value of the vector is vec![0, 3] and the value in the inspect vmo under the foo
43//!   // property is the debug string of the vector "[0, 3]".
44//!   ```
45
46use core::ops::{Deref, DerefMut};
47use derivative::Derivative;
48use fuchsia_inspect::{BoolProperty, Node, Property, StringProperty, UintProperty};
49use std::borrow::{Borrow, Cow};
50use std::collections::HashSet;
51
52/// Generic wrapper for exporting variables via Inspect. Mutations to
53/// the wrapped `value` occur through an `InspectableGuard`, which
54/// notifies the `watcher` when `Drop`ped, transparently keeping the Inspect
55/// state up-to-date.
56///
57/// How the `value` is exported is determined by the `Watch` implementation,
58/// see e.g. `InspectableDebugString`.
59///
60/// Not correct for `V`s with interior mutability, because `Inspectable`
61/// `Deref`s to `V`, mutations to which will bypass the `watcher`.
62#[derive(Derivative)]
63#[derivative(Debug, Eq, PartialEq)]
64pub struct Inspectable<V, W>
65where
66    W: Watch<V>,
67{
68    value: V,
69    #[derivative(Debug = "ignore", PartialEq = "ignore", Hash = "ignore")]
70    watcher: W,
71}
72
73impl<V, W> Inspectable<V, W>
74where
75    W: Watch<V>,
76{
77    /// Creates an `Inspectable` wrapping `value`. Exports `value` via Inspect.
78    pub fn new<'a>(value: V, node: &Node, name: impl Into<Cow<'a, str>>) -> Self {
79        let watcher = W::new(&value, node, name);
80        Self { value, watcher }
81    }
82
83    /// Returns a guard that `DerefMut`s to the wrapped `value`. The `watcher`
84    /// will receive the updated `value` when the guard is `Drop`ped.
85    pub fn get_mut(&mut self) -> InspectableGuard<'_, V, W> {
86        InspectableGuard { value: &mut self.value, watcher: &mut self.watcher }
87    }
88}
89
90impl<V, W> Deref for Inspectable<V, W>
91where
92    W: Watch<V>,
93{
94    type Target = V;
95
96    fn deref(&self) -> &Self::Target {
97        &self.value
98    }
99}
100
101impl<V, W> Borrow<V> for Inspectable<V, W>
102where
103    W: Watch<V>,
104{
105    fn borrow(&self) -> &V {
106        &self.value
107    }
108}
109
110/// Used for exporting an `[Inspectable`][Inspectable]'s wrapped value .
111pub trait Watch<V> {
112    /// Used by [`Inspectable::new()`][Inspectable::new] to create a `Watch`er that exports via
113    /// Inspect the [`Inspectable`][Inspectable]'s wrapped `value`.
114    fn new<'a>(value: &V, node: &Node, name: impl Into<Cow<'a, str>>) -> Self;
115
116    /// Called by [`InspectableGuard`][InspectableGuard] when the guard is dropped, letting the
117    /// `Watch`er update its state with the updated `value`.
118    fn watch(&mut self, value: &V);
119}
120
121/// Calls self.watcher.watch(self.value) when dropped.
122#[must_use]
123pub struct InspectableGuard<'a, V, W>
124where
125    W: Watch<V>,
126{
127    value: &'a mut V,
128    watcher: &'a mut W,
129}
130
131impl<V, W> Deref for InspectableGuard<'_, V, W>
132where
133    W: Watch<V>,
134{
135    type Target = V;
136
137    fn deref(&self) -> &Self::Target {
138        self.value
139    }
140}
141
142impl<V, W> DerefMut for InspectableGuard<'_, V, W>
143where
144    W: Watch<V>,
145{
146    fn deref_mut(&mut self) -> &mut Self::Target {
147        self.value
148    }
149}
150
151impl<V, W> Drop for InspectableGuard<'_, V, W>
152where
153    W: Watch<V>,
154{
155    fn drop(&mut self) {
156        self.watcher.watch(self.value);
157    }
158}
159
160/// Exports via an Inspect `UintProperty` the `len` of the wrapped container.
161#[derive(Debug)]
162pub struct InspectableLenWatcher {
163    len: fuchsia_inspect::UintProperty,
164}
165
166/// Trait implemented by types that can provide a length. Values used for
167/// [`InspectableLen`][InspectableLen] must implement this.
168pub trait Len {
169    fn len(&self) -> usize;
170    fn is_empty(&self) -> bool {
171        self.len() == 0
172    }
173}
174
175impl<V> Watch<V> for InspectableLenWatcher
176where
177    V: Len,
178{
179    fn new<'a>(value: &V, node: &Node, name: impl Into<Cow<'a, str>>) -> Self {
180        Self { len: node.create_uint(name, value.len() as u64) }
181    }
182
183    fn watch(&mut self, value: &V) {
184        self.len.set(value.len() as u64);
185    }
186}
187
188/// Exports via an Inspect `UintProperty` the `len` of the wrapped value `V`.
189pub type InspectableLen<V> = Inspectable<V, InspectableLenWatcher>;
190
191impl<V> Len for Vec<V> {
192    fn len(&self) -> usize {
193        self.len()
194    }
195}
196
197impl<V> Len for HashSet<V> {
198    fn len(&self) -> usize {
199        self.len()
200    }
201}
202
203/// Exports via an Inspect `StringProperty` the `Debug` representation of
204/// the wrapped `value`.
205#[derive(Debug)]
206pub struct InspectableDebugStringWatcher {
207    debug_string: StringProperty,
208}
209
210impl<V> Watch<V> for InspectableDebugStringWatcher
211where
212    V: std::fmt::Debug,
213{
214    fn new<'a>(value: &V, node: &Node, name: impl Into<Cow<'a, str>>) -> Self {
215        Self { debug_string: node.create_string(name, format!("{:?}", value)) }
216    }
217
218    fn watch(&mut self, value: &V) {
219        self.debug_string.set(&format!("{:?}", value))
220    }
221}
222
223/// Exports via an Inspect `StringProperty` the `Debug` representation of the wrapped value `V`.
224pub type InspectableDebugString<V> = Inspectable<V, InspectableDebugStringWatcher>;
225
226/// Exports via an Inspect `UintProperty` a `u64`. Useful because the wrapped `u64`
227/// value can be read.
228#[derive(Debug)]
229pub struct InspectableU64Watcher {
230    uint_property: UintProperty,
231}
232
233impl Watch<u64> for InspectableU64Watcher {
234    fn new<'a>(value: &u64, node: &Node, name: impl Into<Cow<'a, str>>) -> Self {
235        Self { uint_property: node.create_uint(name, *value) }
236    }
237
238    fn watch(&mut self, value: &u64) {
239        self.uint_property.set(*value);
240    }
241}
242
243/// Exports via an Inspect `UintProperty` a `u64`. Useful because the wrapped `u64`
244/// value can be read.
245pub type InspectableU64 = Inspectable<u64, InspectableU64Watcher>;
246
247pub struct InspectableBoolWatcher {
248    bool_property: BoolProperty,
249}
250
251impl Watch<bool> for InspectableBoolWatcher {
252    fn new<'a>(value: &bool, node: &Node, name: impl Into<Cow<'a, str>>) -> Self {
253        Self { bool_property: node.create_bool(name, *value) }
254    }
255
256    fn watch(&mut self, value: &bool) {
257        self.bool_property.set(*value);
258    }
259}
260
261pub type InspectableBool = Inspectable<bool, InspectableBoolWatcher>;
262
263#[cfg(test)]
264mod test {
265    use super::*;
266    use diagnostics_assertions::assert_data_tree;
267    use fuchsia_inspect::{Inspector, IntProperty};
268
269    struct InspectableIntWatcher {
270        i: IntProperty,
271    }
272    impl Watch<i64> for InspectableIntWatcher {
273        fn new<'a>(value: &i64, node: &Node, name: impl Into<Cow<'a, str>>) -> Self {
274            Self { i: node.create_int(name, *value) }
275        }
276        fn watch(&mut self, value: &i64) {
277            self.i.set(*value)
278        }
279    }
280
281    type InspectableInt = Inspectable<i64, InspectableIntWatcher>;
282    fn make_inspectable(i: i64) -> (InspectableInt, Inspector) {
283        let inspector = Inspector::default();
284        let inspectable = InspectableInt::new(i, inspector.root(), "inspectable-int");
285        (inspectable, inspector)
286    }
287
288    #[fuchsia::test]
289    fn test_inspectable_deref() {
290        let inspectable = make_inspectable(1).0;
291
292        assert_eq!(*inspectable, 1);
293    }
294
295    #[fuchsia::test]
296    fn test_guard_deref() {
297        let mut inspectable = make_inspectable(1).0;
298
299        assert_eq!(*inspectable.get_mut(), 1);
300    }
301
302    #[fuchsia::test]
303    fn test_guard_deref_mut() {
304        let mut inspectable = make_inspectable(1).0;
305
306        *inspectable.get_mut() = 2;
307
308        assert_eq!(*inspectable, 2);
309    }
310
311    #[fuchsia::test]
312    fn test_guard_drop_calls_callback() {
313        let (mut inspectable, inspector) = make_inspectable(1);
314
315        let mut guard = inspectable.get_mut();
316        *guard = 2;
317        drop(guard);
318
319        assert_data_tree!(
320            inspector,
321            root: {
322                "inspectable-int": 2i64
323            }
324        );
325    }
326
327    #[fuchsia::test]
328    fn test_partial_eq() {
329        let inspectable0 = make_inspectable(1).0;
330        let inspectable1 = make_inspectable(1).0;
331
332        assert_eq!(inspectable0, inspectable1);
333    }
334
335    #[fuchsia::test]
336    fn test_partial_eq_not() {
337        let inspectable0 = make_inspectable(1).0;
338        let inspectable1 = make_inspectable(2).0;
339
340        assert_ne!(inspectable0, inspectable1);
341    }
342
343    #[fuchsia::test]
344    fn test_borrow() {
345        let inspectable = make_inspectable(1).0;
346
347        assert_eq!(Borrow::<i64>::borrow(&inspectable), &1);
348    }
349}
350
351#[cfg(test)]
352mod test_inspectable_len {
353    use super::*;
354    use diagnostics_assertions::assert_data_tree;
355
356    #[fuchsia::test]
357    fn test_initialization() {
358        let inspector = fuchsia_inspect::Inspector::default();
359        let _inspectable = InspectableLen::new(vec![0], inspector.root(), "test-property");
360
361        assert_data_tree!(
362            inspector,
363            root: {
364                "test-property": 1u64
365            }
366        );
367    }
368
369    #[fuchsia::test]
370    fn test_watcher() {
371        let inspector = fuchsia_inspect::Inspector::default();
372        let mut inspectable = InspectableLen::new(vec![0], inspector.root(), "test-property");
373
374        inspectable.get_mut().push(1);
375
376        assert_data_tree!(
377            inspector,
378            root: {
379                "test-property": 2u64
380            }
381        );
382    }
383}
384
385#[cfg(test)]
386mod test_inspectable_debug_string {
387    use super::*;
388    use diagnostics_assertions::assert_data_tree;
389
390    #[fuchsia::test]
391    fn test_initialization() {
392        let inspector = fuchsia_inspect::Inspector::default();
393        let _inspectable = InspectableDebugString::new(vec![0], inspector.root(), "test-property");
394
395        assert_data_tree!(
396            inspector,
397            root: {
398                "test-property": "[0]"
399            }
400        );
401    }
402
403    #[fuchsia::test]
404    fn test_watcher() {
405        let inspector = fuchsia_inspect::Inspector::default();
406        let mut inspectable =
407            InspectableDebugString::new(vec![0], inspector.root(), "test-property");
408
409        inspectable.get_mut().push(1);
410
411        assert_data_tree!(
412            inspector,
413            root: {
414                "test-property": "[0, 1]"
415            }
416        );
417    }
418}
419
420#[cfg(test)]
421mod test_inspectable_u64 {
422    use super::*;
423    use diagnostics_assertions::assert_data_tree;
424
425    #[fuchsia::test]
426    fn test_initialization() {
427        let inspector = fuchsia_inspect::Inspector::default();
428        let _inspectable = InspectableU64::new(0, inspector.root(), "test-property");
429
430        assert_data_tree!(
431            inspector,
432            root: {
433                "test-property": 0u64,
434            }
435        );
436    }
437
438    #[fuchsia::test]
439    fn test_watcher() {
440        let inspector = fuchsia_inspect::Inspector::default();
441        let mut inspectable = InspectableU64::new(0, inspector.root(), "test-property");
442
443        *inspectable.get_mut() += 1;
444
445        assert_data_tree!(
446            inspector,
447            root: {
448                "test-property": 1u64,
449            }
450        );
451    }
452}
453
454#[cfg(test)]
455mod test_inspectable_bool {
456    use super::*;
457    use diagnostics_assertions::assert_data_tree;
458
459    #[fuchsia::test]
460    fn test_initialization() {
461        let inspector = fuchsia_inspect::Inspector::default();
462        let _inspectable = InspectableBool::new(false, inspector.root(), "test-property");
463
464        assert_data_tree!(
465            inspector,
466            root: {
467                "test-property": false,
468            }
469        );
470    }
471
472    #[fuchsia::test]
473    fn test_watcher() {
474        let inspector = fuchsia_inspect::Inspector::default();
475        let mut inspectable = InspectableBool::new(false, inspector.root(), "test-property");
476
477        *inspectable.get_mut() = true;
478
479        assert_data_tree!(
480            inspector,
481            root: {
482                "test-property": true,
483            }
484        );
485    }
486}