fuchsia_inspect_contrib/inspectable/
mod.rs
1use 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#[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 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 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
110pub trait Watch<V> {
112 fn new<'a>(value: &V, node: &Node, name: impl Into<Cow<'a, str>>) -> Self;
115
116 fn watch(&mut self, value: &V);
119}
120
121#[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#[derive(Debug)]
162pub struct InspectableLenWatcher {
163 len: fuchsia_inspect::UintProperty,
164}
165
166pub 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
188pub 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#[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
223pub type InspectableDebugString<V> = Inspectable<V, InspectableDebugStringWatcher>;
225
226#[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
243pub 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}