driver_tools/subcommands/list_composite_node_specs/
mod.rs1pub mod args;
6
7use crate::common::{node_property_value_to_string, write_node_properties};
8use anyhow::{Context, Result};
9use args::ListCompositeNodeSpecsCommand;
10use flex_fuchsia_driver_development as fdd;
11#[cfg(feature = "fdomain")]
12use fuchsia_driver_dev_fdomain as fuchsia_driver_dev;
13use std::io::Write;
14
15pub async fn list_composite_node_specs(
16 cmd: ListCompositeNodeSpecsCommand,
17 writer: &mut dyn Write,
18 driver_development_proxy: fdd::ManagerProxy,
19) -> Result<()> {
20 writeln!(
21 writer,
22 "WARNING: This command is deprecated. Use `ffx driver composite list` or `ffx driver composite show` instead."
23 )?;
24
25 let composite_infos =
26 fuchsia_driver_dev::get_composite_node_specs(&driver_development_proxy, None)
27 .await
28 .context("Failed to get composite node specs")?;
29
30 let filtered_infos: Vec<_> = if let Some(name_filter) = &cmd.name {
31 composite_infos
32 .into_iter()
33 .filter(|info| {
34 info.spec
35 .as_ref()
36 .and_then(|spec| spec.name.as_ref())
37 .map(|name| name.contains(name_filter))
38 .unwrap_or(false)
39 })
40 .collect()
41 } else {
42 composite_infos
43 };
44
45 if !cmd.verbose {
46 for composite_info in filtered_infos {
47 let name =
48 composite_info.spec.and_then(|spec| spec.name).unwrap_or_else(|| "N/A".to_string());
49 let driver = composite_info
50 .matched_driver
51 .and_then(|matched_driver| matched_driver.composite_driver)
52 .and_then(|composite_driver| composite_driver.driver_info)
53 .and_then(|driver_info| driver_info.url)
54 .unwrap_or_else(|| "None".to_string());
55 writeln!(writer, "{:<20}: {}", name, driver)?;
56 }
57 return Ok(());
58 }
59
60 for composite_info in filtered_infos {
61 let matched_driver = composite_info.matched_driver.unwrap_or_default();
62 let spec = composite_info.spec.unwrap_or_default();
63 if let Some(name) = &spec.name {
64 writeln!(writer, "{0: <10}: {1}", "Name", name)?;
65 }
66
67 let url = matched_driver
68 .composite_driver
69 .as_ref()
70 .and_then(|composite_driver| composite_driver.driver_info.as_ref())
71 .and_then(|driver_info| driver_info.url.clone());
72
73 if let Some(driver) = url {
74 writeln!(writer, "{0: <10}: {1}", "Driver", driver)?;
75 } else {
76 writeln!(writer, "{0: <10}: {1}", "Driver", "None")?;
77 }
78
79 if let Some(nodes) = spec.parents2 {
80 writeln!(writer, "{0: <10}: {1}", "Nodes", nodes.len())?;
81
82 for (i, node) in nodes.into_iter().enumerate() {
83 let name = match &matched_driver.parent_names {
84 Some(names) => format!("\"{}\"", names.get(i).unwrap()),
85 None => "None".to_string(),
86 };
87
88 if &matched_driver.primary_parent_index == &Some(i as u32) {
89 writeln!(writer, "{0: <10}: {1} (Primary)", format!("Node {}", i), name)?;
90 } else {
91 writeln!(writer, "{0: <10}: {1}", format!("Node {}", i), name)?;
92 }
93
94 let bind_rules_len = node.bind_rules.len();
95 writeln!(writer, " {0} {1}", bind_rules_len, "Bind Rules")?;
96
97 for (j, bind_rule) in node.bind_rules.into_iter().enumerate() {
98 let key = &bind_rule.key;
99 let values = bind_rule
100 .values
101 .into_iter()
102 .map(|value| node_property_value_to_string(&value))
103 .collect::<Vec<_>>()
104 .join(", ");
105 writeln!(
106 writer,
107 " [{0:>2}/{1:>2}] : {2:?} {3} {{ {4} }}",
108 j + 1,
109 bind_rules_len,
110 bind_rule.condition,
111 key,
112 values,
113 )?;
114 }
115
116 write_node_properties(&node.properties, writer)?;
117 }
118 }
119
120 writeln!(writer)?;
121 }
122 Ok(())
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use argh::FromArgs;
129 use flex_client::fidl::ServerEnd;
130 use futures::future::{Future, FutureExt};
131 use futures::stream::StreamExt;
132 #[cfg(feature = "fdomain")]
133 use std::sync::Arc;
134 use {flex_fuchsia_driver_framework as fdf, fuchsia_async as fasync};
135
136 async fn test_list_composite_node_specs<F, Fut>(
141 #[cfg(feature = "fdomain")] client: Arc<flex_client::Client>,
142 cmd: ListCompositeNodeSpecsCommand,
143 on_driver_development_request: F,
144 ) -> Result<String>
145 where
146 F: Fn(fdd::ManagerRequest) -> Fut + Send + Sync + 'static,
147 Fut: Future<Output = Result<()>> + Send + Sync,
148 {
149 #[cfg(not(feature = "fdomain"))]
150 let client = flex_client::fidl::ZirconClient;
151 let (driver_development_proxy, mut driver_development_requests) =
152 client.create_proxy_and_stream::<fdd::ManagerMarker>();
153
154 let mut writer = Vec::new();
156 let request_handler_task = fasync::Task::spawn(async move {
157 while let Some(res) = driver_development_requests.next().await {
158 let request = res.unwrap();
159 on_driver_development_request(request).await.context("Failed to handle request")?;
160 }
161 anyhow::bail!("Driver development request stream unexpectedly closed");
162 });
163 futures::select! {
164 res = request_handler_task.fuse() => {
165 res?;
166 anyhow::bail!("Request handler task unexpectedly finished");
167 }
168 res = list_composite_node_specs(cmd, &mut writer, driver_development_proxy).fuse() => res.context("List composite node specs command failed")?,
169 }
170
171 String::from_utf8(writer)
172 .context("Failed to convert list composite node specs output to a string")
173 }
174
175 async fn run_specs_iterator_server(
176 mut specs: Vec<fdf::CompositeInfo>,
177 iterator: ServerEnd<fdd::CompositeNodeSpecIteratorMarker>,
178 ) -> Result<()> {
179 let mut iterator = iterator.into_stream();
180 while let Some(res) = iterator.next().await {
181 let request = res.unwrap();
182 match request {
183 fdd::CompositeNodeSpecIteratorRequest::GetNext { responder } => {
184 responder.send(&specs).unwrap();
185 specs.clear();
186 }
187 }
188 }
189 Ok(())
190 }
191
192 #[fasync::run_singlethreaded(test)]
193 async fn test_verbose() {
194 #[cfg(feature = "fdomain")]
195 let client = fdomain_local::local_client_empty();
196 let cmd = ListCompositeNodeSpecsCommand::from_args(
197 &["list-composite-node-specs"],
198 &["--verbose"],
199 )
200 .unwrap();
201
202 let output = test_list_composite_node_specs(
203 #[cfg(feature = "fdomain")]
204 Arc::clone(&client),
205 cmd,
206 |request: fdd::ManagerRequest| async move {
207 match request {
208 fdd::ManagerRequest::GetCompositeNodeSpecs {
209 name_filter: _,
210 iterator,
211 control_handle: _,
212 } => run_specs_iterator_server(
213 vec![
214 fdf::CompositeInfo {
215 spec: Some(fdf::CompositeNodeSpec {
216 name: Some("test_spec".to_string()),
217 parents2: Some(vec![fdf::ParentSpec2 {
218 bind_rules: vec![fdf::BindRule2 {
219 key: "rule_key".to_string(),
220 condition: fdf::Condition::Accept,
221 values: vec![fdf::NodePropertyValue::StringValue(
222 "rule_val".to_string(),
223 )],
224 }],
225 properties: vec![fdf::NodeProperty2 {
226 key: "prop_key".to_string(),
227 value: fdf::NodePropertyValue::StringValue(
228 "prop_val".to_string(),
229 ),
230 }],
231 }]),
232 ..Default::default()
233 }),
234 matched_driver: None,
235 ..Default::default()
236 },
237 fdf::CompositeInfo {
238 spec: Some(fdf::CompositeNodeSpec {
239 name: Some("test_spec_with_driver".to_string()),
240 parents2: Some(vec![
241 fdf::ParentSpec2 {
242 bind_rules: vec![fdf::BindRule2 {
243 key: "rule_key".to_string(),
244 condition: fdf::Condition::Accept,
245 values: vec![
246 fdf::NodePropertyValue::StringValue(
247 "rule_val".to_string(),
248 ),
249 fdf::NodePropertyValue::StringValue(
250 "rule_val_2".to_string(),
251 ),
252 ],
253 }],
254 properties: vec![fdf::NodeProperty2 {
255 key: "prop_key_0".to_string(),
256 value: fdf::NodePropertyValue::StringValue(
257 "prop_val_0".to_string(),
258 ),
259 }],
260 },
261 fdf::ParentSpec2 {
262 bind_rules: vec![
263 fdf::BindRule2 {
264 key: "0x0001".to_string(),
265 condition: fdf::Condition::Accept,
266 values: vec![
267 fdf::NodePropertyValue::IntValue(0x42),
268 fdf::NodePropertyValue::IntValue(0x123),
269 fdf::NodePropertyValue::IntValue(0x234),
270 ],
271 },
272 fdf::BindRule2 {
273 key: "0xdeadbeef".to_string(),
274 condition: fdf::Condition::Accept,
275 values: vec![fdf::NodePropertyValue::IntValue(
276 0xbeef,
277 )],
278 },
279 ],
280 properties: vec![
281 fdf::NodeProperty2 {
282 key: "prop_key_1".to_string(),
283 value: fdf::NodePropertyValue::EnumValue(
284 "prop_key_1.prop_val".to_string(),
285 ),
286 },
287 fdf::NodeProperty2 {
288 key: "prop_key_2".to_string(),
289 value: fdf::NodePropertyValue::IntValue(0x1),
290 },
291 fdf::NodeProperty2 {
292 key: "prop_key_3".to_string(),
293 value: fdf::NodePropertyValue::BoolValue(true),
294 },
295 ],
296 },
297 ]),
298 ..Default::default()
299 }),
300 matched_driver: Some(fdf::CompositeDriverMatch {
301 composite_driver: Some(fdf::CompositeDriverInfo {
302 driver_info: Some(fdf::DriverInfo {
303 url: Some("driver_url".to_string()),
304 ..Default::default()
305 }),
306 ..Default::default()
307 }),
308 parent_names: Some(vec![
309 "name_one".to_string(),
310 "name_two".to_string(),
311 ]),
312 primary_parent_index: Some(1),
313 ..Default::default()
314 }),
315 ..Default::default()
316 },
317 ],
318 iterator,
319 )
320 .await
321 .context("Failed to run driver info iterator server")?,
322 _ => {}
323 }
324 Ok(())
325 },
326 )
327 .await
328 .unwrap();
329
330 assert_eq!(
331 output,
332 r#"WARNING: This command is deprecated. Use `ffx driver composite list` or `ffx driver composite show` instead.
333Name : test_spec
334Driver : None
335Nodes : 1
336Node 0 : None
337 1 Bind Rules
338 [ 1/ 1] : Accept rule_key { "rule_val" }
339 1 Properties
340 [ 1/ 1] : Key prop_key Value "prop_val"
341
342Name : test_spec_with_driver
343Driver : driver_url
344Nodes : 2
345Node 0 : "name_one"
346 1 Bind Rules
347 [ 1/ 1] : Accept rule_key { "rule_val", "rule_val_2" }
348 1 Properties
349 [ 1/ 1] : Key prop_key_0 Value "prop_val_0"
350Node 1 : "name_two" (Primary)
351 2 Bind Rules
352 [ 1/ 2] : Accept 0x0001 { 0x000042, 0x000123, 0x000234 }
353 [ 2/ 2] : Accept 0xdeadbeef { 0x00beef }
354 3 Properties
355 [ 1/ 3] : Key prop_key_1 Value Enum(prop_key_1.prop_val)
356 [ 2/ 3] : Key prop_key_2 Value 0x000001
357 [ 3/ 3] : Key prop_key_3 Value true
358
359"#
360 );
361 }
362}