xref: /aosp_15_r20/tools/netsim/rust/cli/src/response.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2022 Google LLC
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker 
15*cf78ab8cSAndroid Build Coastguard Worker use std::cmp::max;
16*cf78ab8cSAndroid Build Coastguard Worker 
17*cf78ab8cSAndroid Build Coastguard Worker use crate::args::{self, Beacon, BeaconCreate, BeaconPatch, Capture, Command, OnOffState};
18*cf78ab8cSAndroid Build Coastguard Worker use crate::display::Displayer;
19*cf78ab8cSAndroid Build Coastguard Worker use netsim_common::util::time_display::TimeDisplay;
20*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::{
21*cf78ab8cSAndroid Build Coastguard Worker     common::ChipKind,
22*cf78ab8cSAndroid Build Coastguard Worker     frontend::{CreateDeviceResponse, ListCaptureResponse, ListDeviceResponse, VersionResponse},
23*cf78ab8cSAndroid Build Coastguard Worker     model,
24*cf78ab8cSAndroid Build Coastguard Worker };
25*cf78ab8cSAndroid Build Coastguard Worker use protobuf::Message;
26*cf78ab8cSAndroid Build Coastguard Worker 
27*cf78ab8cSAndroid Build Coastguard Worker impl args::Command {
28*cf78ab8cSAndroid Build Coastguard Worker     /// Format and print the response received from the frontend server for the command
print_response(&self, response: &[u8], verbose: bool)29*cf78ab8cSAndroid Build Coastguard Worker     pub fn print_response(&self, response: &[u8], verbose: bool) {
30*cf78ab8cSAndroid Build Coastguard Worker         match self {
31*cf78ab8cSAndroid Build Coastguard Worker             Command::Version => {
32*cf78ab8cSAndroid Build Coastguard Worker                 Self::print_version_response(VersionResponse::parse_from_bytes(response).unwrap());
33*cf78ab8cSAndroid Build Coastguard Worker             }
34*cf78ab8cSAndroid Build Coastguard Worker             Command::Radio(cmd) => {
35*cf78ab8cSAndroid Build Coastguard Worker                 if verbose {
36*cf78ab8cSAndroid Build Coastguard Worker                     println!(
37*cf78ab8cSAndroid Build Coastguard Worker                         "Radio {} is {} for {}",
38*cf78ab8cSAndroid Build Coastguard Worker                         cmd.radio_type,
39*cf78ab8cSAndroid Build Coastguard Worker                         cmd.status,
40*cf78ab8cSAndroid Build Coastguard Worker                         cmd.name.to_owned()
41*cf78ab8cSAndroid Build Coastguard Worker                     );
42*cf78ab8cSAndroid Build Coastguard Worker                 }
43*cf78ab8cSAndroid Build Coastguard Worker             }
44*cf78ab8cSAndroid Build Coastguard Worker             Command::Move(cmd) => {
45*cf78ab8cSAndroid Build Coastguard Worker                 if verbose {
46*cf78ab8cSAndroid Build Coastguard Worker                     println!(
47*cf78ab8cSAndroid Build Coastguard Worker                         "Moved device:{} to x: {:.2}, y: {:.2}, z: {:.2}",
48*cf78ab8cSAndroid Build Coastguard Worker                         cmd.name,
49*cf78ab8cSAndroid Build Coastguard Worker                         cmd.x,
50*cf78ab8cSAndroid Build Coastguard Worker                         cmd.y,
51*cf78ab8cSAndroid Build Coastguard Worker                         cmd.z.unwrap_or_default()
52*cf78ab8cSAndroid Build Coastguard Worker                     )
53*cf78ab8cSAndroid Build Coastguard Worker                 }
54*cf78ab8cSAndroid Build Coastguard Worker             }
55*cf78ab8cSAndroid Build Coastguard Worker             Command::Devices(_) => {
56*cf78ab8cSAndroid Build Coastguard Worker                 println!(
57*cf78ab8cSAndroid Build Coastguard Worker                     "{}",
58*cf78ab8cSAndroid Build Coastguard Worker                     Displayer::new(
59*cf78ab8cSAndroid Build Coastguard Worker                         ListDeviceResponse::parse_from_bytes(response).unwrap(),
60*cf78ab8cSAndroid Build Coastguard Worker                         verbose
61*cf78ab8cSAndroid Build Coastguard Worker                     )
62*cf78ab8cSAndroid Build Coastguard Worker                 );
63*cf78ab8cSAndroid Build Coastguard Worker             }
64*cf78ab8cSAndroid Build Coastguard Worker             Command::Reset => {
65*cf78ab8cSAndroid Build Coastguard Worker                 if verbose {
66*cf78ab8cSAndroid Build Coastguard Worker                     println!("All devices have been reset.");
67*cf78ab8cSAndroid Build Coastguard Worker                 }
68*cf78ab8cSAndroid Build Coastguard Worker             }
69*cf78ab8cSAndroid Build Coastguard Worker             Command::Capture(Capture::List(cmd)) => Self::print_list_capture_response(
70*cf78ab8cSAndroid Build Coastguard Worker                 ListCaptureResponse::parse_from_bytes(response).unwrap(),
71*cf78ab8cSAndroid Build Coastguard Worker                 verbose,
72*cf78ab8cSAndroid Build Coastguard Worker                 cmd.patterns.to_owned(),
73*cf78ab8cSAndroid Build Coastguard Worker             ),
74*cf78ab8cSAndroid Build Coastguard Worker             Command::Capture(Capture::Patch(cmd)) => {
75*cf78ab8cSAndroid Build Coastguard Worker                 if verbose {
76*cf78ab8cSAndroid Build Coastguard Worker                     println!(
77*cf78ab8cSAndroid Build Coastguard Worker                         "Patched Capture state to {}",
78*cf78ab8cSAndroid Build Coastguard Worker                         Self::on_off_state_to_string(cmd.state),
79*cf78ab8cSAndroid Build Coastguard Worker                     );
80*cf78ab8cSAndroid Build Coastguard Worker                 }
81*cf78ab8cSAndroid Build Coastguard Worker             }
82*cf78ab8cSAndroid Build Coastguard Worker             Command::Capture(Capture::Get(cmd)) => {
83*cf78ab8cSAndroid Build Coastguard Worker                 if verbose {
84*cf78ab8cSAndroid Build Coastguard Worker                     println!("Successfully downloaded file: {}", cmd.current_file);
85*cf78ab8cSAndroid Build Coastguard Worker                 }
86*cf78ab8cSAndroid Build Coastguard Worker             }
87*cf78ab8cSAndroid Build Coastguard Worker             Command::Gui => {
88*cf78ab8cSAndroid Build Coastguard Worker                 unimplemented!("No Grpc Response for Gui Command.");
89*cf78ab8cSAndroid Build Coastguard Worker             }
90*cf78ab8cSAndroid Build Coastguard Worker             Command::Artifact => {
91*cf78ab8cSAndroid Build Coastguard Worker                 unimplemented!("No Grpc Response for Artifact Command.");
92*cf78ab8cSAndroid Build Coastguard Worker             }
93*cf78ab8cSAndroid Build Coastguard Worker             Command::Beacon(action) => match action {
94*cf78ab8cSAndroid Build Coastguard Worker                 Beacon::Create(kind) => match kind {
95*cf78ab8cSAndroid Build Coastguard Worker                     BeaconCreate::Ble(_) => {
96*cf78ab8cSAndroid Build Coastguard Worker                         if !verbose {
97*cf78ab8cSAndroid Build Coastguard Worker                             return;
98*cf78ab8cSAndroid Build Coastguard Worker                         }
99*cf78ab8cSAndroid Build Coastguard Worker                         let device = CreateDeviceResponse::parse_from_bytes(response)
100*cf78ab8cSAndroid Build Coastguard Worker                             .expect("could not read device from response")
101*cf78ab8cSAndroid Build Coastguard Worker                             .device;
102*cf78ab8cSAndroid Build Coastguard Worker 
103*cf78ab8cSAndroid Build Coastguard Worker                         if device.chips.len() == 1 {
104*cf78ab8cSAndroid Build Coastguard Worker                             println!(
105*cf78ab8cSAndroid Build Coastguard Worker                                 "Created device '{}' with ble beacon chip '{}'",
106*cf78ab8cSAndroid Build Coastguard Worker                                 device.name, device.chips[0].name
107*cf78ab8cSAndroid Build Coastguard Worker                             );
108*cf78ab8cSAndroid Build Coastguard Worker                         } else {
109*cf78ab8cSAndroid Build Coastguard Worker                             panic!("the gRPC request completed successfully but the response contained an unexpected number of chips");
110*cf78ab8cSAndroid Build Coastguard Worker                         }
111*cf78ab8cSAndroid Build Coastguard Worker                     }
112*cf78ab8cSAndroid Build Coastguard Worker                 },
113*cf78ab8cSAndroid Build Coastguard Worker                 Beacon::Patch(kind) => {
114*cf78ab8cSAndroid Build Coastguard Worker                     match kind {
115*cf78ab8cSAndroid Build Coastguard Worker                         BeaconPatch::Ble(args) => {
116*cf78ab8cSAndroid Build Coastguard Worker                             if !verbose {
117*cf78ab8cSAndroid Build Coastguard Worker                                 return;
118*cf78ab8cSAndroid Build Coastguard Worker                             }
119*cf78ab8cSAndroid Build Coastguard Worker                             if let Some(advertise_mode) = &args.settings.advertise_mode {
120*cf78ab8cSAndroid Build Coastguard Worker                                 match advertise_mode {
121*cf78ab8cSAndroid Build Coastguard Worker                                     args::Interval::Mode(mode) => {
122*cf78ab8cSAndroid Build Coastguard Worker                                         println!("Set advertise mode to {:#?}", mode)
123*cf78ab8cSAndroid Build Coastguard Worker                                     }
124*cf78ab8cSAndroid Build Coastguard Worker                                     args::Interval::Milliseconds(ms) => {
125*cf78ab8cSAndroid Build Coastguard Worker                                         println!("Set advertise interval to {} ms", ms)
126*cf78ab8cSAndroid Build Coastguard Worker                                     }
127*cf78ab8cSAndroid Build Coastguard Worker                                 }
128*cf78ab8cSAndroid Build Coastguard Worker                             }
129*cf78ab8cSAndroid Build Coastguard Worker                             if let Some(tx_power_level) = &args.settings.tx_power_level {
130*cf78ab8cSAndroid Build Coastguard Worker                                 match tx_power_level {
131*cf78ab8cSAndroid Build Coastguard Worker                                     args::TxPower::Level(level) => {
132*cf78ab8cSAndroid Build Coastguard Worker                                         println!("Set transmit power level to {:#?}", level)
133*cf78ab8cSAndroid Build Coastguard Worker                                     }
134*cf78ab8cSAndroid Build Coastguard Worker                                     args::TxPower::Dbm(dbm) => {
135*cf78ab8cSAndroid Build Coastguard Worker                                         println!("Set transmit power level to {} dBm", dbm)
136*cf78ab8cSAndroid Build Coastguard Worker                                     }
137*cf78ab8cSAndroid Build Coastguard Worker                                 }
138*cf78ab8cSAndroid Build Coastguard Worker                             }
139*cf78ab8cSAndroid Build Coastguard Worker                             if args.settings.scannable {
140*cf78ab8cSAndroid Build Coastguard Worker                                 println!("Set scannable to true");
141*cf78ab8cSAndroid Build Coastguard Worker                             }
142*cf78ab8cSAndroid Build Coastguard Worker                             if let Some(timeout) = args.settings.timeout {
143*cf78ab8cSAndroid Build Coastguard Worker                                 println!("Set timeout to {} ms", timeout);
144*cf78ab8cSAndroid Build Coastguard Worker                             }
145*cf78ab8cSAndroid Build Coastguard Worker                             if args.advertise_data.include_device_name {
146*cf78ab8cSAndroid Build Coastguard Worker                                 println!("Added the device's name to the advertise packet")
147*cf78ab8cSAndroid Build Coastguard Worker                             }
148*cf78ab8cSAndroid Build Coastguard Worker                             if args.advertise_data.include_tx_power_level {
149*cf78ab8cSAndroid Build Coastguard Worker                                 println!("Added the beacon's transmit power level to the advertise packet")
150*cf78ab8cSAndroid Build Coastguard Worker                             }
151*cf78ab8cSAndroid Build Coastguard Worker                             if args.advertise_data.manufacturer_data.is_some() {
152*cf78ab8cSAndroid Build Coastguard Worker                                 println!("Added manufacturer data to the advertise packet")
153*cf78ab8cSAndroid Build Coastguard Worker                             }
154*cf78ab8cSAndroid Build Coastguard Worker                             if args.settings.scannable {
155*cf78ab8cSAndroid Build Coastguard Worker                                 println!("Set scannable to true");
156*cf78ab8cSAndroid Build Coastguard Worker                             }
157*cf78ab8cSAndroid Build Coastguard Worker                             if let Some(timeout) = args.settings.timeout {
158*cf78ab8cSAndroid Build Coastguard Worker                                 println!("Set timeout to {} ms", timeout);
159*cf78ab8cSAndroid Build Coastguard Worker                             }
160*cf78ab8cSAndroid Build Coastguard Worker                         }
161*cf78ab8cSAndroid Build Coastguard Worker                     }
162*cf78ab8cSAndroid Build Coastguard Worker                 }
163*cf78ab8cSAndroid Build Coastguard Worker                 Beacon::Remove(args) => {
164*cf78ab8cSAndroid Build Coastguard Worker                     if !verbose {
165*cf78ab8cSAndroid Build Coastguard Worker                         return;
166*cf78ab8cSAndroid Build Coastguard Worker                     }
167*cf78ab8cSAndroid Build Coastguard Worker                     if let Some(chip_name) = &args.chip_name {
168*cf78ab8cSAndroid Build Coastguard Worker                         println!("Removed chip '{}' from device '{}'", chip_name, args.device_name)
169*cf78ab8cSAndroid Build Coastguard Worker                     } else {
170*cf78ab8cSAndroid Build Coastguard Worker                         println!("Removed device '{}'", args.device_name)
171*cf78ab8cSAndroid Build Coastguard Worker                     }
172*cf78ab8cSAndroid Build Coastguard Worker                 }
173*cf78ab8cSAndroid Build Coastguard Worker             },
174*cf78ab8cSAndroid Build Coastguard Worker             Command::Bumble => {
175*cf78ab8cSAndroid Build Coastguard Worker                 unimplemented!("No Grpc Response for Bumble Command.");
176*cf78ab8cSAndroid Build Coastguard Worker             }
177*cf78ab8cSAndroid Build Coastguard Worker         }
178*cf78ab8cSAndroid Build Coastguard Worker     }
179*cf78ab8cSAndroid Build Coastguard Worker 
capture_state_to_string(state: Option<bool>) -> String180*cf78ab8cSAndroid Build Coastguard Worker     fn capture_state_to_string(state: Option<bool>) -> String {
181*cf78ab8cSAndroid Build Coastguard Worker         state.map(|value| if value { "on" } else { "off" }).unwrap_or("unknown").to_string()
182*cf78ab8cSAndroid Build Coastguard Worker     }
183*cf78ab8cSAndroid Build Coastguard Worker 
on_off_state_to_string(state: OnOffState) -> String184*cf78ab8cSAndroid Build Coastguard Worker     fn on_off_state_to_string(state: OnOffState) -> String {
185*cf78ab8cSAndroid Build Coastguard Worker         match state {
186*cf78ab8cSAndroid Build Coastguard Worker             OnOffState::On => "on".to_string(),
187*cf78ab8cSAndroid Build Coastguard Worker             OnOffState::Off => "off".to_string(),
188*cf78ab8cSAndroid Build Coastguard Worker         }
189*cf78ab8cSAndroid Build Coastguard Worker     }
190*cf78ab8cSAndroid Build Coastguard Worker 
191*cf78ab8cSAndroid Build Coastguard Worker     /// Helper function to format and print VersionResponse
print_version_response(response: VersionResponse)192*cf78ab8cSAndroid Build Coastguard Worker     fn print_version_response(response: VersionResponse) {
193*cf78ab8cSAndroid Build Coastguard Worker         println!("Netsim version: {}", response.version);
194*cf78ab8cSAndroid Build Coastguard Worker     }
195*cf78ab8cSAndroid Build Coastguard Worker 
196*cf78ab8cSAndroid Build Coastguard Worker     /// Helper function to format and print ListCaptureResponse
print_list_capture_response( mut response: ListCaptureResponse, verbose: bool, patterns: Vec<String>, )197*cf78ab8cSAndroid Build Coastguard Worker     fn print_list_capture_response(
198*cf78ab8cSAndroid Build Coastguard Worker         mut response: ListCaptureResponse,
199*cf78ab8cSAndroid Build Coastguard Worker         verbose: bool,
200*cf78ab8cSAndroid Build Coastguard Worker         patterns: Vec<String>,
201*cf78ab8cSAndroid Build Coastguard Worker     ) {
202*cf78ab8cSAndroid Build Coastguard Worker         if response.captures.is_empty() {
203*cf78ab8cSAndroid Build Coastguard Worker             if verbose {
204*cf78ab8cSAndroid Build Coastguard Worker                 println!("No available Capture found.");
205*cf78ab8cSAndroid Build Coastguard Worker             }
206*cf78ab8cSAndroid Build Coastguard Worker             return;
207*cf78ab8cSAndroid Build Coastguard Worker         }
208*cf78ab8cSAndroid Build Coastguard Worker         if patterns.is_empty() {
209*cf78ab8cSAndroid Build Coastguard Worker             println!("List of Captures:");
210*cf78ab8cSAndroid Build Coastguard Worker         } else {
211*cf78ab8cSAndroid Build Coastguard Worker             // Filter out list of captures with matching patterns
212*cf78ab8cSAndroid Build Coastguard Worker             Self::filter_captures(&mut response.captures, &patterns);
213*cf78ab8cSAndroid Build Coastguard Worker             if response.captures.is_empty() {
214*cf78ab8cSAndroid Build Coastguard Worker                 if verbose {
215*cf78ab8cSAndroid Build Coastguard Worker                     println!("No available Capture found matching pattern(s) `{:?}`:", patterns);
216*cf78ab8cSAndroid Build Coastguard Worker                 }
217*cf78ab8cSAndroid Build Coastguard Worker                 return;
218*cf78ab8cSAndroid Build Coastguard Worker             }
219*cf78ab8cSAndroid Build Coastguard Worker             println!("List of Captures matching pattern(s) `{:?}`:", patterns);
220*cf78ab8cSAndroid Build Coastguard Worker         }
221*cf78ab8cSAndroid Build Coastguard Worker         // Create the header row and determine column widths
222*cf78ab8cSAndroid Build Coastguard Worker         let id_hdr = "ID";
223*cf78ab8cSAndroid Build Coastguard Worker         let name_hdr = "Device Name";
224*cf78ab8cSAndroid Build Coastguard Worker         let chipkind_hdr = "Chip Kind";
225*cf78ab8cSAndroid Build Coastguard Worker         let state_hdr = "State";
226*cf78ab8cSAndroid Build Coastguard Worker         let time_hdr = "Timestamp";
227*cf78ab8cSAndroid Build Coastguard Worker         let records_hdr = "Records";
228*cf78ab8cSAndroid Build Coastguard Worker         let size_hdr = "Size (bytes)";
229*cf78ab8cSAndroid Build Coastguard Worker         let id_width = 4; // ID width of 4 since capture id (=chip_id) starts at 1000
230*cf78ab8cSAndroid Build Coastguard Worker         let state_width = 8; // State width of 8 for 'detached' if device is disconnected
231*cf78ab8cSAndroid Build Coastguard Worker         let chipkind_width = 11; // ChipKind width 11 for 'UNSPECIFIED'
232*cf78ab8cSAndroid Build Coastguard Worker         let time_width = 9; // Timestamp width 9 for header (value format set to HH:MM:SS)
233*cf78ab8cSAndroid Build Coastguard Worker         let name_width = max(
234*cf78ab8cSAndroid Build Coastguard Worker             (response.captures.iter().max_by_key(|x| x.device_name.len()))
235*cf78ab8cSAndroid Build Coastguard Worker                 .unwrap_or_default()
236*cf78ab8cSAndroid Build Coastguard Worker                 .device_name
237*cf78ab8cSAndroid Build Coastguard Worker                 .len(),
238*cf78ab8cSAndroid Build Coastguard Worker             name_hdr.len(),
239*cf78ab8cSAndroid Build Coastguard Worker         );
240*cf78ab8cSAndroid Build Coastguard Worker         let records_width = max(
241*cf78ab8cSAndroid Build Coastguard Worker             (response.captures.iter().max_by_key(|x| x.records))
242*cf78ab8cSAndroid Build Coastguard Worker                 .unwrap_or_default()
243*cf78ab8cSAndroid Build Coastguard Worker                 .records
244*cf78ab8cSAndroid Build Coastguard Worker                 .to_string()
245*cf78ab8cSAndroid Build Coastguard Worker                 .len(),
246*cf78ab8cSAndroid Build Coastguard Worker             records_hdr.len(),
247*cf78ab8cSAndroid Build Coastguard Worker         );
248*cf78ab8cSAndroid Build Coastguard Worker         let size_width = max(
249*cf78ab8cSAndroid Build Coastguard Worker             (response.captures.iter().max_by_key(|x| x.size))
250*cf78ab8cSAndroid Build Coastguard Worker                 .unwrap_or_default()
251*cf78ab8cSAndroid Build Coastguard Worker                 .size
252*cf78ab8cSAndroid Build Coastguard Worker                 .to_string()
253*cf78ab8cSAndroid Build Coastguard Worker                 .len(),
254*cf78ab8cSAndroid Build Coastguard Worker             size_hdr.len(),
255*cf78ab8cSAndroid Build Coastguard Worker         );
256*cf78ab8cSAndroid Build Coastguard Worker         // Print header for capture list
257*cf78ab8cSAndroid Build Coastguard Worker         println!(
258*cf78ab8cSAndroid Build Coastguard Worker             "{}",
259*cf78ab8cSAndroid Build Coastguard Worker             if verbose {
260*cf78ab8cSAndroid Build Coastguard Worker                 format!("{:id_width$} | {:name_width$} | {:chipkind_width$} | {:state_width$} | {:time_width$} | {:records_width$} | {:size_width$} |",
261*cf78ab8cSAndroid Build Coastguard Worker                     id_hdr,
262*cf78ab8cSAndroid Build Coastguard Worker                     name_hdr,
263*cf78ab8cSAndroid Build Coastguard Worker                     chipkind_hdr,
264*cf78ab8cSAndroid Build Coastguard Worker                     state_hdr,
265*cf78ab8cSAndroid Build Coastguard Worker                     time_hdr,
266*cf78ab8cSAndroid Build Coastguard Worker                     records_hdr,
267*cf78ab8cSAndroid Build Coastguard Worker                     size_hdr,
268*cf78ab8cSAndroid Build Coastguard Worker                 )
269*cf78ab8cSAndroid Build Coastguard Worker             } else {
270*cf78ab8cSAndroid Build Coastguard Worker                 format!(
271*cf78ab8cSAndroid Build Coastguard Worker                     "{:name_width$} | {:chipkind_width$} | {:state_width$} | {:records_width$} |",
272*cf78ab8cSAndroid Build Coastguard Worker                     name_hdr, chipkind_hdr, state_hdr, records_hdr
273*cf78ab8cSAndroid Build Coastguard Worker                 )
274*cf78ab8cSAndroid Build Coastguard Worker             }
275*cf78ab8cSAndroid Build Coastguard Worker         );
276*cf78ab8cSAndroid Build Coastguard Worker         // Print information of each Capture
277*cf78ab8cSAndroid Build Coastguard Worker         for capture in &response.captures {
278*cf78ab8cSAndroid Build Coastguard Worker             println!(
279*cf78ab8cSAndroid Build Coastguard Worker                 "{}",
280*cf78ab8cSAndroid Build Coastguard Worker                 if verbose {
281*cf78ab8cSAndroid Build Coastguard Worker                     format!("{:id_width$} | {:name_width$} | {:chipkind_width$} | {:state_width$} | {:time_width$} | {:records_width$} | {:size_width$} |",
282*cf78ab8cSAndroid Build Coastguard Worker                         capture.id.to_string(),
283*cf78ab8cSAndroid Build Coastguard Worker                         capture.device_name,
284*cf78ab8cSAndroid Build Coastguard Worker                         Self::chip_kind_to_string(capture.chip_kind.enum_value_or_default()),
285*cf78ab8cSAndroid Build Coastguard Worker                         if capture.valid {Self::capture_state_to_string(capture.state)} else {"detached".to_string()},
286*cf78ab8cSAndroid Build Coastguard Worker                         TimeDisplay::new(
287*cf78ab8cSAndroid Build Coastguard Worker                             capture.timestamp.get_or_default().seconds,
288*cf78ab8cSAndroid Build Coastguard Worker                             capture.timestamp.get_or_default().nanos as u32,
289*cf78ab8cSAndroid Build Coastguard Worker                         ).utc_display_hms(),
290*cf78ab8cSAndroid Build Coastguard Worker                         capture.records,
291*cf78ab8cSAndroid Build Coastguard Worker                         capture.size,
292*cf78ab8cSAndroid Build Coastguard Worker                     )
293*cf78ab8cSAndroid Build Coastguard Worker                 } else {
294*cf78ab8cSAndroid Build Coastguard Worker                     format!(
295*cf78ab8cSAndroid Build Coastguard Worker                         "{:name_width$} | {:chipkind_width$} | {:state_width$} | {:records_width$} |",
296*cf78ab8cSAndroid Build Coastguard Worker                         capture.device_name,
297*cf78ab8cSAndroid Build Coastguard Worker                         Self::chip_kind_to_string(capture.chip_kind.enum_value_or_default()),
298*cf78ab8cSAndroid Build Coastguard Worker                         if capture.valid {Self::capture_state_to_string(capture.state)} else {"detached".to_string()},
299*cf78ab8cSAndroid Build Coastguard Worker                         capture.records,
300*cf78ab8cSAndroid Build Coastguard Worker                     )
301*cf78ab8cSAndroid Build Coastguard Worker                 }
302*cf78ab8cSAndroid Build Coastguard Worker             );
303*cf78ab8cSAndroid Build Coastguard Worker         }
304*cf78ab8cSAndroid Build Coastguard Worker     }
305*cf78ab8cSAndroid Build Coastguard Worker 
chip_kind_to_string(chip_kind: ChipKind) -> String306*cf78ab8cSAndroid Build Coastguard Worker     pub fn chip_kind_to_string(chip_kind: ChipKind) -> String {
307*cf78ab8cSAndroid Build Coastguard Worker         match chip_kind {
308*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::UNSPECIFIED => "UNSPECIFIED".to_string(),
309*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::BLUETOOTH => "BLUETOOTH".to_string(),
310*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::WIFI => "WIFI".to_string(),
311*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::UWB => "UWB".to_string(),
312*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::BLUETOOTH_BEACON => "BLUETOOTH_BEACON".to_string(),
313*cf78ab8cSAndroid Build Coastguard Worker         }
314*cf78ab8cSAndroid Build Coastguard Worker     }
315*cf78ab8cSAndroid Build Coastguard Worker 
filter_captures(captures: &mut Vec<model::Capture>, keys: &[String])316*cf78ab8cSAndroid Build Coastguard Worker     pub fn filter_captures(captures: &mut Vec<model::Capture>, keys: &[String]) {
317*cf78ab8cSAndroid Build Coastguard Worker         // Filter out list of captures with matching pattern
318*cf78ab8cSAndroid Build Coastguard Worker         captures.retain(|capture| {
319*cf78ab8cSAndroid Build Coastguard Worker             keys.iter().map(|key| key.to_uppercase()).all(|key| {
320*cf78ab8cSAndroid Build Coastguard Worker                 capture.id.to_string().contains(&key)
321*cf78ab8cSAndroid Build Coastguard Worker                     || capture.device_name.to_uppercase().contains(&key)
322*cf78ab8cSAndroid Build Coastguard Worker                     || Self::chip_kind_to_string(capture.chip_kind.enum_value_or_default())
323*cf78ab8cSAndroid Build Coastguard Worker                         .contains(&key)
324*cf78ab8cSAndroid Build Coastguard Worker             })
325*cf78ab8cSAndroid Build Coastguard Worker         });
326*cf78ab8cSAndroid Build Coastguard Worker     }
327*cf78ab8cSAndroid Build Coastguard Worker }
328*cf78ab8cSAndroid Build Coastguard Worker 
329*cf78ab8cSAndroid Build Coastguard Worker #[cfg(test)]
330*cf78ab8cSAndroid Build Coastguard Worker mod tests {
331*cf78ab8cSAndroid Build Coastguard Worker     use super::*;
test_filter_captures_helper(patterns: Vec<String>, expected_captures: Vec<model::Capture>)332*cf78ab8cSAndroid Build Coastguard Worker     fn test_filter_captures_helper(patterns: Vec<String>, expected_captures: Vec<model::Capture>) {
333*cf78ab8cSAndroid Build Coastguard Worker         let mut captures = all_test_captures();
334*cf78ab8cSAndroid Build Coastguard Worker         Command::filter_captures(&mut captures, &patterns);
335*cf78ab8cSAndroid Build Coastguard Worker         assert_eq!(captures, expected_captures);
336*cf78ab8cSAndroid Build Coastguard Worker     }
337*cf78ab8cSAndroid Build Coastguard Worker 
capture_1() -> model::Capture338*cf78ab8cSAndroid Build Coastguard Worker     fn capture_1() -> model::Capture {
339*cf78ab8cSAndroid Build Coastguard Worker         model::Capture {
340*cf78ab8cSAndroid Build Coastguard Worker             id: 4001,
341*cf78ab8cSAndroid Build Coastguard Worker             chip_kind: ChipKind::BLUETOOTH.into(),
342*cf78ab8cSAndroid Build Coastguard Worker             device_name: "device 1".to_string(),
343*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
344*cf78ab8cSAndroid Build Coastguard Worker         }
345*cf78ab8cSAndroid Build Coastguard Worker     }
capture_1_wifi() -> model::Capture346*cf78ab8cSAndroid Build Coastguard Worker     fn capture_1_wifi() -> model::Capture {
347*cf78ab8cSAndroid Build Coastguard Worker         model::Capture {
348*cf78ab8cSAndroid Build Coastguard Worker             id: 4002,
349*cf78ab8cSAndroid Build Coastguard Worker             chip_kind: ChipKind::WIFI.into(),
350*cf78ab8cSAndroid Build Coastguard Worker             device_name: "device 1".to_string(),
351*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
352*cf78ab8cSAndroid Build Coastguard Worker         }
353*cf78ab8cSAndroid Build Coastguard Worker     }
capture_2() -> model::Capture354*cf78ab8cSAndroid Build Coastguard Worker     fn capture_2() -> model::Capture {
355*cf78ab8cSAndroid Build Coastguard Worker         model::Capture {
356*cf78ab8cSAndroid Build Coastguard Worker             id: 4003,
357*cf78ab8cSAndroid Build Coastguard Worker             chip_kind: ChipKind::BLUETOOTH.into(),
358*cf78ab8cSAndroid Build Coastguard Worker             device_name: "device 2".to_string(),
359*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
360*cf78ab8cSAndroid Build Coastguard Worker         }
361*cf78ab8cSAndroid Build Coastguard Worker     }
capture_3() -> model::Capture362*cf78ab8cSAndroid Build Coastguard Worker     fn capture_3() -> model::Capture {
363*cf78ab8cSAndroid Build Coastguard Worker         model::Capture {
364*cf78ab8cSAndroid Build Coastguard Worker             id: 4004,
365*cf78ab8cSAndroid Build Coastguard Worker             chip_kind: ChipKind::WIFI.into(),
366*cf78ab8cSAndroid Build Coastguard Worker             device_name: "device 3".to_string(),
367*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
368*cf78ab8cSAndroid Build Coastguard Worker         }
369*cf78ab8cSAndroid Build Coastguard Worker     }
capture_4_uwb() -> model::Capture370*cf78ab8cSAndroid Build Coastguard Worker     fn capture_4_uwb() -> model::Capture {
371*cf78ab8cSAndroid Build Coastguard Worker         model::Capture {
372*cf78ab8cSAndroid Build Coastguard Worker             id: 4005,
373*cf78ab8cSAndroid Build Coastguard Worker             chip_kind: ChipKind::UWB.into(),
374*cf78ab8cSAndroid Build Coastguard Worker             device_name: "device 4".to_string(),
375*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
376*cf78ab8cSAndroid Build Coastguard Worker         }
377*cf78ab8cSAndroid Build Coastguard Worker     }
all_test_captures() -> Vec<model::Capture>378*cf78ab8cSAndroid Build Coastguard Worker     fn all_test_captures() -> Vec<model::Capture> {
379*cf78ab8cSAndroid Build Coastguard Worker         vec![capture_1(), capture_1_wifi(), capture_2(), capture_3(), capture_4_uwb()]
380*cf78ab8cSAndroid Build Coastguard Worker     }
381*cf78ab8cSAndroid Build Coastguard Worker 
382*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_no_match()383*cf78ab8cSAndroid Build Coastguard Worker     fn test_no_match() {
384*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["test".to_string()], vec![]);
385*cf78ab8cSAndroid Build Coastguard Worker     }
386*cf78ab8cSAndroid Build Coastguard Worker 
387*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_all_match()388*cf78ab8cSAndroid Build Coastguard Worker     fn test_all_match() {
389*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["device".to_string()], all_test_captures());
390*cf78ab8cSAndroid Build Coastguard Worker     }
391*cf78ab8cSAndroid Build Coastguard Worker 
392*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_match_capture_id()393*cf78ab8cSAndroid Build Coastguard Worker     fn test_match_capture_id() {
394*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["4001".to_string()], vec![capture_1()]);
395*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["03".to_string()], vec![capture_2()]);
396*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["40".to_string()], all_test_captures());
397*cf78ab8cSAndroid Build Coastguard Worker     }
398*cf78ab8cSAndroid Build Coastguard Worker 
399*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_match_device_name()400*cf78ab8cSAndroid Build Coastguard Worker     fn test_match_device_name() {
401*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(
402*cf78ab8cSAndroid Build Coastguard Worker             vec!["device 1".to_string()],
403*cf78ab8cSAndroid Build Coastguard Worker             vec![capture_1(), capture_1_wifi()],
404*cf78ab8cSAndroid Build Coastguard Worker         );
405*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec![" 2".to_string()], vec![capture_2()]);
406*cf78ab8cSAndroid Build Coastguard Worker     }
407*cf78ab8cSAndroid Build Coastguard Worker 
408*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_match_device_name_case_insensitive()409*cf78ab8cSAndroid Build Coastguard Worker     fn test_match_device_name_case_insensitive() {
410*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(
411*cf78ab8cSAndroid Build Coastguard Worker             vec!["DEVICE 1".to_string()],
412*cf78ab8cSAndroid Build Coastguard Worker             vec![capture_1(), capture_1_wifi()],
413*cf78ab8cSAndroid Build Coastguard Worker         );
414*cf78ab8cSAndroid Build Coastguard Worker     }
415*cf78ab8cSAndroid Build Coastguard Worker 
416*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_match_wifi()417*cf78ab8cSAndroid Build Coastguard Worker     fn test_match_wifi() {
418*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["wifi".to_string()], vec![capture_1_wifi(), capture_3()]);
419*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["WIFI".to_string()], vec![capture_1_wifi(), capture_3()]);
420*cf78ab8cSAndroid Build Coastguard Worker     }
421*cf78ab8cSAndroid Build Coastguard Worker 
422*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_match_uwb()423*cf78ab8cSAndroid Build Coastguard Worker     fn test_match_uwb() {
424*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["uwb".to_string()], vec![capture_4_uwb()]);
425*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["UWB".to_string()], vec![capture_4_uwb()]);
426*cf78ab8cSAndroid Build Coastguard Worker     }
427*cf78ab8cSAndroid Build Coastguard Worker 
428*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_match_bt()429*cf78ab8cSAndroid Build Coastguard Worker     fn test_match_bt() {
430*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["BLUETOOTH".to_string()], vec![capture_1(), capture_2()]);
431*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(vec!["blue".to_string()], vec![capture_1(), capture_2()]);
432*cf78ab8cSAndroid Build Coastguard Worker     }
433*cf78ab8cSAndroid Build Coastguard Worker 
434*cf78ab8cSAndroid Build Coastguard Worker     #[test]
test_match_name_and_chip()435*cf78ab8cSAndroid Build Coastguard Worker     fn test_match_name_and_chip() {
436*cf78ab8cSAndroid Build Coastguard Worker         test_filter_captures_helper(
437*cf78ab8cSAndroid Build Coastguard Worker             vec!["device 1".to_string(), "wifi".to_string()],
438*cf78ab8cSAndroid Build Coastguard Worker             vec![capture_1_wifi()],
439*cf78ab8cSAndroid Build Coastguard Worker         );
440*cf78ab8cSAndroid Build Coastguard Worker     }
441*cf78ab8cSAndroid Build Coastguard Worker }
442