xref: /aosp_15_r20/tools/netsim/rust/cli/src/display.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use netsim_proto::frontend::ListDeviceResponse;
16 use netsim_proto::model::{
17     self,
18     chip::ble_beacon::advertise_settings,
19     chip::ble_beacon::{AdvertiseData, AdvertiseSettings},
20 };
21 use protobuf::MessageField;
22 use std::fmt;
23 
24 const INDENT_WIDTH: usize = 2;
25 
26 /// Displayer for model protobufs. Implements fmt::Display.
27 /// # Invariants
28 /// Displayed values **do not** end in a newline.
29 pub struct Displayer<T> {
30     value: T,
31     verbose: bool,
32     indent: usize,
33 }
34 
35 impl<T> Displayer<T> {
36     /// Returns a new displayer for values of the provided type.
new(value: T, verbose: bool) -> Self37     pub fn new(value: T, verbose: bool) -> Self {
38         Displayer { value, verbose, indent: 0 }
39     }
40 
41     /// Indent the displayed string by a given amount. Returns `self`.
indent(&mut self, current_indent: usize) -> &Self42     pub fn indent(&mut self, current_indent: usize) -> &Self {
43         self.indent = current_indent + INDENT_WIDTH;
44         self
45     }
46 }
47 
48 impl fmt::Display for Displayer<ListDeviceResponse> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result49     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50         let indent = self.indent;
51 
52         let mut devices = self.value.devices.iter().peekable();
53         while let Some(device) = devices.next() {
54             write!(f, "{:indent$}{}", "", Displayer::new(device, self.verbose))?;
55             if devices.peek().is_some() {
56                 // We print the newline here instead of in the Device displayer because we don't want a newline before the very first device.
57                 writeln!(f)?;
58             }
59         }
60 
61         Ok(())
62     }
63 }
64 
65 impl fmt::Display for Displayer<&model::Device> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result66     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67         let indent = self.indent;
68         let width = 9;
69 
70         write!(
71             f,
72             "{:indent$}{:width$} {}",
73             "",
74             self.value.name,
75             Displayer::new(self.value.position.as_ref().unwrap_or_default(), self.verbose)
76         )?;
77 
78         for chip in self.value.chips.iter() {
79             write!(f, "{:indent$}{}", "", Displayer::new(chip, self.verbose).indent(self.indent))?;
80         }
81 
82         Ok(())
83     }
84 }
85 
86 impl fmt::Display for Displayer<&model::Chip> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result87     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88         let indent = self.indent;
89         let width = 9;
90 
91         match self.value.chip.as_ref() {
92             Some(model::chip::Chip::BleBeacon(ble_beacon)) => {
93                 let radio = ble_beacon.bt.low_energy.as_ref().unwrap_or_default();
94                 let beacon_width = 16 + width;
95 
96                 if self.verbose || !radio.state.unwrap_or_default() {
97                     writeln!(f)?;
98                     write!(
99                         f,
100                         "{:indent$}{:beacon_width$} {}",
101                         "",
102                         format!("beacon-ble ({}):", self.value.name),
103                         Displayer::new(radio, self.verbose),
104                     )?;
105                 }
106 
107                 if self.verbose {
108                     writeln!(f)?;
109                     write!(f, "{}", Displayer::new(ble_beacon, self.verbose).indent(self.indent))?;
110                 }
111             }
112             Some(model::chip::Chip::Bt(bt)) => {
113                 if let Some(ble) = bt.low_energy.as_ref() {
114                     if self.verbose || !ble.state.unwrap_or_default() {
115                         writeln!(f)?;
116                         write!(
117                             f,
118                             "{:indent$}{:width$}{}",
119                             "",
120                             "ble: ",
121                             Displayer::new(ble, self.verbose),
122                         )?;
123                     }
124                 };
125 
126                 if let Some(classic) = bt.classic.as_ref() {
127                     if self.verbose || !classic.state.unwrap_or_default() {
128                         writeln!(f)?;
129                         write!(
130                             f,
131                             "{:indent$}{:width$}{}",
132                             "",
133                             "classic: ",
134                             Displayer::new(classic, self.verbose),
135                         )?;
136                     }
137                 };
138             }
139             Some(model::chip::Chip::Wifi(wifi)) => {
140                 if self.verbose || !wifi.state.unwrap_or_default() {
141                     writeln!(f)?;
142                     write!(
143                         f,
144                         "{:indent$}{:width$}{}",
145                         "",
146                         "wifi: ",
147                         Displayer::new(wifi, self.verbose)
148                     )?;
149                 }
150             }
151             Some(model::chip::Chip::Uwb(uwb)) => {
152                 if self.verbose || !uwb.state.unwrap_or_default() {
153                     writeln!(f)?;
154                     write!(
155                         f,
156                         "{:indent$}{:width$}{}",
157                         "",
158                         "uwb: ",
159                         Displayer::new(uwb, self.verbose)
160                     )?;
161                 }
162             }
163             _ => {
164                 if self.verbose {
165                     writeln!(f)?;
166                     write!(f, "{:indent$}Unknown chip", "")?
167                 }
168             }
169         }
170 
171         Ok(())
172     }
173 }
174 
175 impl fmt::Display for Displayer<&model::chip::BleBeacon> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result176     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177         let indent = self.indent;
178         let address_width = 16;
179         write!(f, "{:indent$}address: {:address_width$}", "", self.value.address)?;
180 
181         if self.value.settings.is_some()
182             && self.value.settings != MessageField::some(AdvertiseSettings::default())
183         {
184             writeln!(f)?;
185             write!(
186                 f,
187                 "{:indent$}advertise settings:{}",
188                 "",
189                 Displayer::new(self.value.settings.as_ref().unwrap_or_default(), self.verbose)
190                     .indent(self.indent)
191             )?;
192         }
193 
194         if self.value.adv_data.is_some()
195             && self.value.adv_data != MessageField::some(AdvertiseData::default())
196         {
197             writeln!(f)?;
198             write!(
199                 f,
200                 "{:indent$}advertise packet data:{}",
201                 "",
202                 Displayer::new(self.value.adv_data.as_ref().unwrap_or_default(), self.verbose)
203                     .indent(self.indent)
204             )?;
205         }
206 
207         // TODO(jmes): Add scan response data.
208 
209         Ok(())
210     }
211 }
212 
213 impl fmt::Display for Displayer<&model::Position> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result214     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215         let indent = self.indent;
216         let precision = 2;
217 
218         if self.verbose
219             || (self.value.x != f32::default()
220                 || self.value.y != f32::default()
221                 || self.value.z != f32::default())
222         {
223             write!(
224                 f,
225                 "{:indent$} position: {:.precision$}, {:.precision$}, {:.precision$}",
226                 "", self.value.x, self.value.y, self.value.z,
227             )?;
228         }
229 
230         Ok(())
231     }
232 }
233 
234 impl fmt::Display for Displayer<&model::chip::ble_beacon::AdvertiseSettings> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result235     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236         let indent = self.indent;
237         let width = 25;
238 
239         if let Some(tx_power) = self.value.tx_power.as_ref() {
240             writeln!(f)?;
241             write!(f, "{:indent$}{}", "", Displayer::new(tx_power, self.verbose))?;
242         }
243 
244         if let Some(interval) = self.value.interval.as_ref() {
245             writeln!(f)?;
246             write!(f, "{:indent$}{}", "", Displayer::new(interval, self.verbose))?;
247         }
248 
249         if self.value.scannable {
250             writeln!(f)?;
251             write!(f, "{:indent$}{:width$}: {}", "", "scannable", self.value.scannable)?;
252         }
253 
254         if self.value.timeout != u64::default() {
255             writeln!(f)?;
256             write!(f, "{:indent$}{:width$}: {} ms", "", "timeout", self.value.timeout)?;
257         }
258 
259         Ok(())
260     }
261 }
262 
263 impl fmt::Display for Displayer<&model::chip::ble_beacon::AdvertiseData> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result264     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265         let indent = self.indent;
266         let width = 25;
267 
268         if self.value.include_device_name != bool::default() {
269             writeln!(f)?;
270             write!(
271                 f,
272                 "{:indent$}{:width$}: {}",
273                 "", "include device name", self.value.include_device_name
274             )?;
275         }
276 
277         if self.value.include_tx_power_level != bool::default() {
278             writeln!(f)?;
279             write!(
280                 f,
281                 "{:indent$}{:width$}: {}",
282                 "", "include tx power level", self.value.include_tx_power_level
283             )?;
284         }
285 
286         if self.value.manufacturer_data != Vec::<u8>::default() {
287             writeln!(f)?;
288             write!(
289                 f,
290                 "{:indent$}{:width$}: {}",
291                 "",
292                 "manufacturer data bytes",
293                 self.value.manufacturer_data.len()
294             )?;
295         }
296 
297         Ok(())
298     }
299 }
300 
301 impl fmt::Display for Displayer<&advertise_settings::Interval> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result302     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303         let indent = self.indent;
304         let width = 25;
305 
306         match self.value {
307             advertise_settings::Interval::Milliseconds(interval) => {
308                 write!(f, "{:indent$}{:width$}: {} ms", "", "interval", interval)
309             }
310             advertise_settings::Interval::AdvertiseMode(mode) => {
311                 write!(f, "{:indent$}{:width$}: {:?}", "", "advertise mode", mode)
312             }
313             _ => Err(fmt::Error),
314         }
315     }
316 }
317 
318 impl fmt::Display for Displayer<&advertise_settings::Tx_power> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result319     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320         let indent = self.indent;
321         let width = 25;
322 
323         match self.value {
324             advertise_settings::Tx_power::Dbm(dbm) => {
325                 write!(f, "{:indent$}{:width$}: {} dBm", "", "tx power", dbm)
326             }
327             advertise_settings::Tx_power::TxPowerLevel(level) => {
328                 write!(f, "{:indent$}{:width$}: {:?}", "", "tx power level", level)
329             }
330             _ => Err(fmt::Error),
331         }
332     }
333 }
334 
335 impl fmt::Display for Displayer<&model::chip::Radio> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result336     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337         let indent = self.indent;
338         let count_width = 9;
339         write!(f, "{:indent$}{}", "", Displayer::new(&self.value.state, self.verbose),)?;
340 
341         if self.verbose {
342             write!(
343                 f,
344                 "| rx_count: {:count_width$} | tx_count: {:count_width$}",
345                 self.value.rx_count, self.value.tx_count
346             )?
347         }
348 
349         Ok(())
350     }
351 }
352 
353 impl fmt::Display for Displayer<&Option<bool>> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result354     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355         let indent = self.indent;
356         let width = 9;
357 
358         write!(
359             f,
360             "{:indent$}{:width$}",
361             "",
362             match self.value {
363                 Some(true) => "up",
364                 Some(false) => "down",
365                 None => "unknown",
366             }
367         )
368     }
369 }
370