blob: 692c05b0b5d679ed8557305d4728f1a0e69d6874 [file]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {fuchsia_inspect as inspect, fuchsia_inspect_contrib::nodes::ManagedNode, std::fmt};
const FALSE_VALUE: u64 = 0;
const TRUE_VALUE: u64 = 1;
/// Convert a type to the correct supported Inspect Property type. This is used in Bluetooth to
/// ensure consistent representations of values in Inspect.
///
/// Note: It represents them appropriately for Bluetooth but may not be the appropriate type
/// for other use cases. It shouldn't be used outside of the Bluetooth project.
pub trait ToProperty {
type PropertyType;
fn to_property(&self) -> Self::PropertyType;
}
impl ToProperty for bool {
type PropertyType = u64;
fn to_property(&self) -> Self::PropertyType {
if *self {
TRUE_VALUE
} else {
FALSE_VALUE
}
}
}
impl ToProperty for Option<bool> {
type PropertyType = u64;
fn to_property(&self) -> Self::PropertyType {
self.as_ref().map(bool::to_property).unwrap_or(FALSE_VALUE)
}
}
impl ToProperty for String {
type PropertyType = String;
fn to_property(&self) -> Self::PropertyType {
self.to_string()
}
}
impl<T, V> ToProperty for Vec<T>
where
T: ToProperty<PropertyType = V>,
V: ToString,
{
type PropertyType = String;
fn to_property(&self) -> Self::PropertyType {
self.iter()
.map(|t| <T as ToProperty>::to_property(t).to_string())
.collect::<Vec<String>>()
.join(", ")
}
}
/// Vectors of T show up as a comma separated list string property. `None` types are
/// represented as an empty string.
impl<T, V> ToProperty for Option<Vec<T>>
where
T: ToProperty<PropertyType = V>,
V: ToString,
{
type PropertyType = String;
fn to_property(&self) -> Self::PropertyType {
self.as_ref().map(ToProperty::to_property).unwrap_or_else(String::new)
}
}
/// Convenience function to create a string containing the debug representation of an object that
/// implements `Debug`
pub trait DebugExt {
fn debug(&self) -> String;
}
impl<T: fmt::Debug> DebugExt for T {
fn debug(&self) -> String {
format!("{:?}", self)
}
}
/// Represents inspect data that is tied to a specific object. This inspect data and the object of
/// type T should always be bundled together.
pub trait InspectData<T> {
fn new(object: &T, inspect: inspect::Node) -> Self;
}
pub trait IsInspectable
where
Self: Sized + Send + Sync + 'static,
{
type I: InspectData<Self>;
}
/// A wrapper around a type T that bundles some inspect data alongside instances of the type.
#[derive(Debug)]
pub struct Inspectable<T: IsInspectable> {
pub(crate) inner: T,
pub(crate) inspect: T::I,
}
impl<T: IsInspectable> Inspectable<T> {
/// Create a new instance of an `Inspectable` wrapper type containing the T instance that
/// it wraps along with populated inspect data.
pub fn new(object: T, inspect: inspect::Node) -> Inspectable<T> {
Inspectable { inspect: T::I::new(&object, inspect), inner: object }
}
}
/// `Inspectable`s can always safely be immutably dereferenced as the type T that they wrap
/// because the data will not be mutated through this reference.
impl<T: IsInspectable> std::ops::Deref for Inspectable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
/// A trait representing the inspect data for a type T that will never be mutated. This trait
/// allows for a simpler "fire and forget" representation of the inspect data associated with an
/// object. This is because inspect handles for the data will never need to be accessed after
/// creation.
pub trait ImmutableDataInspect<T> {
fn new(data: &T, manager: ManagedNode) -> Self;
}
/// "Fire and forget" representation of some inspect data that does not allow access inspect
/// handles after they are created.
pub struct ImmutableDataInspectManager {
pub(crate) _manager: ManagedNode,
}
impl<T, I: ImmutableDataInspect<T>> InspectData<T> for I {
/// Create a new instance of some type `I` that represents the immutable inspect data for a type
/// `T`. This is done by handing `I` a `ManagedNode` instead of a `Node` and calling into the
/// monomorphized version of ImmutableDataInspect<T> for I.
fn new(data: &T, inspect: inspect::Node) -> I {
I::new(data, ManagedNode::new(inspect))
}
}
/// A placeholder node that can be used in tests that do not care about the `Node` value
pub fn placeholder_node() -> fuchsia_inspect::Node {
fuchsia_inspect::Inspector::new().root().create_child("placeholder")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bool_to_property() {
let b = false.to_property();
assert_eq!(b, FALSE_VALUE);
let b = true.to_property();
assert_eq!(b, TRUE_VALUE);
}
#[test]
fn optional_bool_to_property() {
let b: u64 = None::<bool>.to_property();
assert_eq!(b, FALSE_VALUE);
let b = Some(false).to_property();
assert_eq!(b, FALSE_VALUE);
let b = Some(true).to_property();
assert_eq!(b, TRUE_VALUE);
}
#[test]
fn string_vec_to_property() {
let s = Vec::<String>::new().to_property();
assert_eq!(s, "");
let s = vec!["foo".to_string()].to_property();
assert_eq!(s, "foo");
let s = vec!["foo".to_string(), "bar".to_string(), "baz".to_string()].to_property();
assert_eq!(s, "foo, bar, baz");
}
#[test]
fn optional_string_vec_to_property() {
let s = Some(vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]).to_property();
assert_eq!(s, "foo, bar, baz");
}
#[test]
fn debug_string() {
#[derive(Debug)]
struct Foo {
bar: u8,
baz: &'static str,
}
let foo = Foo { bar: 1, baz: "baz value" };
assert_eq!(format!("{:?}", foo), foo.debug());
}
}