blob: 983c03015ca698909300fa96357aaf4f434bff07 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use netsim_proto::frontend::ListDeviceResponse;
use netsim_proto::model::{
self,
chip::ble_beacon::advertise_settings,
chip::ble_beacon::{AdvertiseData, AdvertiseSettings},
};
use protobuf::MessageField;
use std::fmt;
const INDENT_WIDTH: usize = 2;
/// Displayer for model protobufs. Implements fmt::Display.
/// # Invariants
/// Displayed values **do not** end in a newline.
pub struct Displayer<T> {
value: T,
verbose: bool,
indent: usize,
}
impl<T> Displayer<T> {
/// Returns a new displayer for values of the provided type.
pub fn new(value: T, verbose: bool) -> Self {
Displayer { value, verbose, indent: 0 }
}
/// Indent the displayed string by a given amount. Returns `self`.
pub fn indent(&mut self, current_indent: usize) -> &Self {
self.indent = current_indent + INDENT_WIDTH;
self
}
}
impl fmt::Display for Displayer<ListDeviceResponse> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let mut devices = self.value.devices.iter().peekable();
while let Some(device) = devices.next() {
write!(f, "{:indent$}{}", "", Displayer::new(device, self.verbose))?;
if devices.peek().is_some() {
// We print the newline here instead of in the Device displayer because we don't want a newline before the very first device.
writeln!(f)?;
}
}
Ok(())
}
}
impl fmt::Display for Displayer<&model::Device> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let width = 9;
write!(
f,
"{:indent$}{:width$} {}",
"",
self.value.name,
Displayer::new(self.value.position.as_ref().unwrap_or_default(), self.verbose)
)?;
for chip in self.value.chips.iter() {
write!(f, "{:indent$}{}", "", Displayer::new(chip, self.verbose).indent(self.indent))?;
}
Ok(())
}
}
impl fmt::Display for Displayer<&model::Chip> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let width = 9;
match self.value.chip.as_ref() {
Some(model::chip::Chip::BleBeacon(ble_beacon)) => {
let radio = ble_beacon.bt.low_energy.as_ref().unwrap_or_default();
let beacon_width = 16 + width;
if self.verbose || radio.state.enum_value_or_default() == model::State::OFF {
writeln!(f)?;
write!(
f,
"{:indent$}{:beacon_width$} {}",
"",
format!("beacon-ble ({}):", self.value.name),
Displayer::new(radio, self.verbose),
)?;
}
if self.verbose {
writeln!(f)?;
write!(f, "{}", Displayer::new(ble_beacon, self.verbose).indent(self.indent))?;
}
}
Some(model::chip::Chip::Bt(bt)) => {
if let Some(ble) = bt.low_energy.as_ref() {
if self.verbose || ble.state.enum_value_or_default() == model::State::OFF {
writeln!(f)?;
write!(
f,
"{:indent$}{:width$}{}",
"",
"ble: ",
Displayer::new(ble, self.verbose),
)?;
}
};
if let Some(classic) = bt.classic.as_ref() {
if self.verbose || classic.state.enum_value_or_default() == model::State::OFF {
writeln!(f)?;
write!(
f,
"{:indent$}{:width$}{}",
"",
"classic: ",
Displayer::new(classic, self.verbose),
)?;
}
};
}
Some(model::chip::Chip::Wifi(wifi)) => {
if self.verbose || wifi.state.enum_value_or_default() == model::State::OFF {
writeln!(f)?;
write!(
f,
"{:indent$}{:width$}{}",
"",
"wifi: ",
Displayer::new(wifi, self.verbose)
)?;
}
}
Some(model::chip::Chip::Uwb(uwb)) => {
if self.verbose || uwb.state.enum_value_or_default() == model::State::OFF {
writeln!(f)?;
write!(
f,
"{:indent$}{:width$}{}",
"",
"uwb: ",
Displayer::new(uwb, self.verbose)
)?;
}
}
_ => {
if self.verbose {
writeln!(f)?;
write!(f, "{:indent$}Unknown chip", "")?
}
}
}
Ok(())
}
}
impl fmt::Display for Displayer<&model::chip::BleBeacon> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let address_width = 16;
write!(f, "{:indent$}address: {:address_width$}", "", self.value.address)?;
if self.value.settings.is_some()
&& self.value.settings != MessageField::some(AdvertiseSettings::default())
{
writeln!(f)?;
write!(
f,
"{:indent$}advertise settings:{}",
"",
Displayer::new(self.value.settings.as_ref().unwrap_or_default(), self.verbose)
.indent(self.indent)
)?;
}
if self.value.adv_data.is_some()
&& self.value.adv_data != MessageField::some(AdvertiseData::default())
{
writeln!(f)?;
write!(
f,
"{:indent$}advertise packet data:{}",
"",
Displayer::new(self.value.adv_data.as_ref().unwrap_or_default(), self.verbose)
.indent(self.indent)
)?;
}
// TODO(jmes): Add scan response data.
Ok(())
}
}
impl fmt::Display for Displayer<&model::Position> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let precision = 2;
if self.verbose
|| (self.value.x != f32::default()
|| self.value.y != f32::default()
|| self.value.z != f32::default())
{
write!(
f,
"{:indent$} position: {:.precision$}, {:.precision$}, {:.precision$}",
"", self.value.x, self.value.y, self.value.z,
)?;
}
Ok(())
}
}
impl fmt::Display for Displayer<&model::chip::ble_beacon::AdvertiseSettings> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let width = 25;
if let Some(tx_power) = self.value.tx_power.as_ref() {
writeln!(f)?;
write!(f, "{:indent$}{}", "", Displayer::new(tx_power, self.verbose))?;
}
if let Some(interval) = self.value.interval.as_ref() {
writeln!(f)?;
write!(f, "{:indent$}{}", "", Displayer::new(interval, self.verbose))?;
}
if self.value.scannable {
writeln!(f)?;
write!(f, "{:indent$}{:width$}: {}", "", "scannable", self.value.scannable)?;
}
if self.value.timeout != u64::default() {
writeln!(f)?;
write!(f, "{:indent$}{:width$}: {} ms", "", "timeout", self.value.timeout)?;
}
Ok(())
}
}
impl fmt::Display for Displayer<&model::chip::ble_beacon::AdvertiseData> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let width = 25;
if self.value.include_device_name != bool::default() {
writeln!(f)?;
write!(
f,
"{:indent$}{:width$}: {}",
"", "include device name", self.value.include_device_name
)?;
}
if self.value.include_tx_power_level != bool::default() {
writeln!(f)?;
write!(
f,
"{:indent$}{:width$}: {}",
"", "include tx power level", self.value.include_tx_power_level
)?;
}
if self.value.manufacturer_data != Vec::<u8>::default() {
writeln!(f)?;
write!(
f,
"{:indent$}{:width$}: {}",
"",
"manufacturer data bytes",
self.value.manufacturer_data.len()
)?;
}
Ok(())
}
}
impl fmt::Display for Displayer<&advertise_settings::Interval> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let width = 25;
match self.value {
advertise_settings::Interval::Milliseconds(interval) => {
write!(f, "{:indent$}{:width$}: {} ms", "", "interval", interval)
}
advertise_settings::Interval::AdvertiseMode(mode) => {
write!(f, "{:indent$}{:width$}: {:?}", "", "advertise mode", mode)
}
_ => Err(fmt::Error),
}
}
}
impl fmt::Display for Displayer<&advertise_settings::Tx_power> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let width = 25;
match self.value {
advertise_settings::Tx_power::Dbm(dbm) => {
write!(f, "{:indent$}{:width$}: {} dBm", "", "tx power", dbm)
}
advertise_settings::Tx_power::TxPowerLevel(level) => {
write!(f, "{:indent$}{:width$}: {:?}", "", "tx power level", level)
}
_ => Err(fmt::Error),
}
}
}
impl fmt::Display for Displayer<&model::chip::Radio> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let count_width = 9;
write!(
f,
"{:indent$}{}",
"",
Displayer::new(&self.value.state.enum_value_or_default(), self.verbose),
)?;
if self.verbose {
write!(
f,
"| rx_count: {:count_width$} | tx_count: {:count_width$}",
self.value.rx_count, self.value.tx_count
)?
}
Ok(())
}
}
impl fmt::Display for Displayer<&model::State> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = self.indent;
let width = 9;
write!(
f,
"{:indent$}{:width$}",
"",
match self.value {
model::State::ON => "up",
model::State::OFF => "down",
_ => "unknown",
}
)
}
}