fuchsia_inspect_contrib/inspectable/
mod.rsuse core::ops::{Deref, DerefMut};
use derivative::Derivative;
use fuchsia_inspect::{
BoolProperty, Node, Property, StringProperty, StringReference, UintProperty,
};
use std::borrow::Borrow;
use std::collections::HashSet;
#[derive(Derivative)]
#[derivative(Debug, Eq, PartialEq)]
pub struct Inspectable<V, W>
where
W: Watch<V>,
{
value: V,
#[derivative(Debug = "ignore", PartialEq = "ignore", Hash = "ignore")]
watcher: W,
}
impl<V, W> Inspectable<V, W>
where
W: Watch<V>,
{
pub fn new(value: V, node: &Node, name: impl Into<StringReference>) -> Self {
let watcher = W::new(&value, node, name);
Self { value, watcher }
}
pub fn get_mut(&mut self) -> InspectableGuard<'_, V, W> {
InspectableGuard { value: &mut self.value, watcher: &mut self.watcher }
}
}
impl<V, W> Deref for Inspectable<V, W>
where
W: Watch<V>,
{
type Target = V;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<V, W> Borrow<V> for Inspectable<V, W>
where
W: Watch<V>,
{
fn borrow(&self) -> &V {
&self.value
}
}
pub trait Watch<V> {
fn new(value: &V, node: &Node, name: impl Into<StringReference>) -> Self;
fn watch(&mut self, value: &V);
}
#[must_use]
pub struct InspectableGuard<'a, V, W>
where
W: Watch<V>,
{
value: &'a mut V,
watcher: &'a mut W,
}
impl<'a, V, W> Deref for InspectableGuard<'a, V, W>
where
W: Watch<V>,
{
type Target = V;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<'a, V, W> DerefMut for InspectableGuard<'a, V, W>
where
W: Watch<V>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.value
}
}
impl<'a, V, W> Drop for InspectableGuard<'a, V, W>
where
W: Watch<V>,
{
fn drop(&mut self) {
self.watcher.watch(self.value);
}
}
#[derive(Debug)]
pub struct InspectableLenWatcher {
len: fuchsia_inspect::UintProperty,
}
pub trait Len {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<V> Watch<V> for InspectableLenWatcher
where
V: Len,
{
fn new(value: &V, node: &Node, name: impl Into<StringReference>) -> Self {
Self { len: node.create_uint(name, value.len() as u64) }
}
fn watch(&mut self, value: &V) {
self.len.set(value.len() as u64);
}
}
pub type InspectableLen<V> = Inspectable<V, InspectableLenWatcher>;
impl<V> Len for Vec<V> {
fn len(&self) -> usize {
self.len()
}
}
impl<V> Len for HashSet<V> {
fn len(&self) -> usize {
self.len()
}
}
#[derive(Debug)]
pub struct InspectableDebugStringWatcher {
debug_string: StringProperty,
}
impl<V> Watch<V> for InspectableDebugStringWatcher
where
V: std::fmt::Debug,
{
fn new(value: &V, node: &Node, name: impl Into<StringReference>) -> Self {
Self { debug_string: node.create_string(name, format!("{:?}", value)) }
}
fn watch(&mut self, value: &V) {
self.debug_string.set(&format!("{:?}", value))
}
}
pub type InspectableDebugString<V> = Inspectable<V, InspectableDebugStringWatcher>;
#[derive(Debug)]
pub struct InspectableU64Watcher {
uint_property: UintProperty,
}
impl Watch<u64> for InspectableU64Watcher {
fn new(value: &u64, node: &Node, name: impl Into<StringReference>) -> Self {
Self { uint_property: node.create_uint(name, *value) }
}
fn watch(&mut self, value: &u64) {
self.uint_property.set(*value);
}
}
pub type InspectableU64 = Inspectable<u64, InspectableU64Watcher>;
pub struct InspectableBoolWatcher {
bool_property: BoolProperty,
}
impl Watch<bool> for InspectableBoolWatcher {
fn new(value: &bool, node: &Node, name: impl Into<StringReference>) -> Self {
Self { bool_property: node.create_bool(name, *value) }
}
fn watch(&mut self, value: &bool) {
self.bool_property.set(*value);
}
}
pub type InspectableBool = Inspectable<bool, InspectableBoolWatcher>;
#[cfg(test)]
mod test {
use super::*;
use diagnostics_assertions::assert_data_tree;
use fuchsia_inspect::{Inspector, IntProperty};
struct InspectableIntWatcher {
i: IntProperty,
}
impl Watch<i64> for InspectableIntWatcher {
fn new(value: &i64, node: &Node, name: impl Into<StringReference>) -> Self {
Self { i: node.create_int(name, *value) }
}
fn watch(&mut self, value: &i64) {
self.i.set(*value)
}
}
type InspectableInt = Inspectable<i64, InspectableIntWatcher>;
fn make_inspectable(i: i64) -> (InspectableInt, Inspector) {
let inspector = Inspector::default();
let inspectable = InspectableInt::new(i, inspector.root(), "inspectable-int");
(inspectable, inspector)
}
#[fuchsia::test]
fn test_inspectable_deref() {
let inspectable = make_inspectable(1).0;
assert_eq!(*inspectable, 1);
}
#[fuchsia::test]
fn test_guard_deref() {
let mut inspectable = make_inspectable(1).0;
assert_eq!(*inspectable.get_mut(), 1);
}
#[fuchsia::test]
fn test_guard_deref_mut() {
let mut inspectable = make_inspectable(1).0;
*inspectable.get_mut() = 2;
assert_eq!(*inspectable, 2);
}
#[fuchsia::test]
fn test_guard_drop_calls_callback() {
let (mut inspectable, inspector) = make_inspectable(1);
let mut guard = inspectable.get_mut();
*guard = 2;
drop(guard);
assert_data_tree!(
inspector,
root: {
"inspectable-int": 2i64
}
);
}
#[fuchsia::test]
fn test_partial_eq() {
let inspectable0 = make_inspectable(1).0;
let inspectable1 = make_inspectable(1).0;
assert_eq!(inspectable0, inspectable1);
}
#[fuchsia::test]
fn test_partial_eq_not() {
let inspectable0 = make_inspectable(1).0;
let inspectable1 = make_inspectable(2).0;
assert_ne!(inspectable0, inspectable1);
}
#[fuchsia::test]
fn test_borrow() {
let inspectable = make_inspectable(1).0;
assert_eq!(Borrow::<i64>::borrow(&inspectable), &1);
}
}
#[cfg(test)]
mod test_inspectable_len {
use super::*;
use diagnostics_assertions::assert_data_tree;
#[fuchsia::test]
fn test_initialization() {
let inspector = fuchsia_inspect::Inspector::default();
let _inspectable = InspectableLen::new(vec![0], inspector.root(), "test-property");
assert_data_tree!(
inspector,
root: {
"test-property": 1u64
}
);
}
#[fuchsia::test]
fn test_watcher() {
let inspector = fuchsia_inspect::Inspector::default();
let mut inspectable = InspectableLen::new(vec![0], inspector.root(), "test-property");
inspectable.get_mut().push(1);
assert_data_tree!(
inspector,
root: {
"test-property": 2u64
}
);
}
}
#[cfg(test)]
mod test_inspectable_debug_string {
use super::*;
use diagnostics_assertions::assert_data_tree;
#[fuchsia::test]
fn test_initialization() {
let inspector = fuchsia_inspect::Inspector::default();
let _inspectable = InspectableDebugString::new(vec![0], inspector.root(), "test-property");
assert_data_tree!(
inspector,
root: {
"test-property": "[0]"
}
);
}
#[fuchsia::test]
fn test_watcher() {
let inspector = fuchsia_inspect::Inspector::default();
let mut inspectable =
InspectableDebugString::new(vec![0], inspector.root(), "test-property");
inspectable.get_mut().push(1);
assert_data_tree!(
inspector,
root: {
"test-property": "[0, 1]"
}
);
}
}
#[cfg(test)]
mod test_inspectable_u64 {
use super::*;
use diagnostics_assertions::assert_data_tree;
#[fuchsia::test]
fn test_initialization() {
let inspector = fuchsia_inspect::Inspector::default();
let _inspectable = InspectableU64::new(0, inspector.root(), "test-property");
assert_data_tree!(
inspector,
root: {
"test-property": 0u64,
}
);
}
#[fuchsia::test]
fn test_watcher() {
let inspector = fuchsia_inspect::Inspector::default();
let mut inspectable = InspectableU64::new(0, inspector.root(), "test-property");
*inspectable.get_mut() += 1;
assert_data_tree!(
inspector,
root: {
"test-property": 1u64,
}
);
}
}
#[cfg(test)]
mod test_inspectable_bool {
use super::*;
use diagnostics_assertions::assert_data_tree;
#[fuchsia::test]
fn test_initialization() {
let inspector = fuchsia_inspect::Inspector::default();
let _inspectable = InspectableBool::new(false, inspector.root(), "test-property");
assert_data_tree!(
inspector,
root: {
"test-property": false,
}
);
}
#[fuchsia::test]
fn test_watcher() {
let inspector = fuchsia_inspect::Inspector::default();
let mut inspectable = InspectableBool::new(false, inspector.root(), "test-property");
*inspectable.get_mut() = true;
assert_data_tree!(
inspector,
root: {
"test-property": true,
}
);
}
}