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 flex_fuchsia_driver_framework as fdf;
131 use fuchsia_async as fasync;
132 use futures::future::{Future, FutureExt};
133 use futures::stream::StreamExt;
134 #[cfg(feature = "fdomain")]
135 use std::sync::Arc;
136
137 async fn test_list_composite_node_specs<F, Fut>(
142 #[cfg(feature = "fdomain")] client: Arc<flex_client::Client>,
143 cmd: ListCompositeNodeSpecsCommand,
144 on_driver_development_request: F,
145 ) -> Result<String>
146 where
147 F: Fn(fdd::ManagerRequest) -> Fut + Send + Sync + 'static,
148 Fut: Future<Output = Result<()>> + Send + Sync,
149 {
150 #[cfg(not(feature = "fdomain"))]
151 let client = flex_client::fidl::ZirconClient;
152 let (driver_development_proxy, mut driver_development_requests) =
153 client.create_proxy_and_stream::<fdd::ManagerMarker>();
154
155 let mut writer = Vec::new();
157 let request_handler_task = fasync::Task::spawn(async move {
158 while let Some(res) = driver_development_requests.next().await {
159 let request = res.unwrap();
160 on_driver_development_request(request).await.context("Failed to handle request")?;
161 }
162 anyhow::bail!("Driver development request stream unexpectedly closed");
163 });
164 futures::select! {
165 res = request_handler_task.fuse() => {
166 res?;
167 anyhow::bail!("Request handler task unexpectedly finished");
168 }
169 res = list_composite_node_specs(cmd, &mut writer, driver_development_proxy).fuse() => res.context("List composite node specs command failed")?,
170 }
171
172 String::from_utf8(writer)
173 .context("Failed to convert list composite node specs output to a string")
174 }
175
176 async fn run_specs_iterator_server(
177 mut specs: Vec<fdf::CompositeInfo>,
178 iterator: ServerEnd<fdd::CompositeNodeSpecIteratorMarker>,
179 ) -> Result<()> {
180 let mut iterator = iterator.into_stream();
181 while let Some(res) = iterator.next().await {
182 let request = res.unwrap();
183 match request {
184 fdd::CompositeNodeSpecIteratorRequest::GetNext { responder } => {
185 responder.send(&specs).unwrap();
186 specs.clear();
187 }
188 }
189 }
190 Ok(())
191 }
192
193 #[fuchsia::test]
194 async fn test_verbose() {
195 #[cfg(feature = "fdomain")]
196 let client = fdomain_local::local_client_empty();
197 let cmd = ListCompositeNodeSpecsCommand::from_args(
198 &["list-composite-node-specs"],
199 &["--verbose"],
200 )
201 .unwrap();
202
203 let output = test_list_composite_node_specs(
204 #[cfg(feature = "fdomain")]
205 Arc::clone(&client),
206 cmd,
207 |request: fdd::ManagerRequest| async move {
208 match request {
209 fdd::ManagerRequest::GetCompositeNodeSpecs {
210 name_filter: _,
211 iterator,
212 control_handle: _,
213 } => run_specs_iterator_server(
214 vec![
215 fdf::CompositeInfo {
216 spec: Some(fdf::CompositeNodeSpec {
217 name: Some("test_spec".to_string()),
218 parents2: Some(vec![fdf::ParentSpec2 {
219 bind_rules: vec![fdf::BindRule2 {
220 key: "rule_key".to_string(),
221 condition: fdf::Condition::Accept,
222 values: vec![fdf::NodePropertyValue::StringValue(
223 "rule_val".to_string(),
224 )],
225 }],
226 properties: vec![fdf::NodeProperty2 {
227 key: "prop_key".to_string(),
228 value: fdf::NodePropertyValue::StringValue(
229 "prop_val".to_string(),
230 ),
231 }],
232 }]),
233 ..Default::default()
234 }),
235 matched_driver: None,
236 ..Default::default()
237 },
238 fdf::CompositeInfo {
239 spec: Some(fdf::CompositeNodeSpec {
240 name: Some("test_spec_with_driver".to_string()),
241 parents2: Some(vec![
242 fdf::ParentSpec2 {
243 bind_rules: vec![fdf::BindRule2 {
244 key: "rule_key".to_string(),
245 condition: fdf::Condition::Accept,
246 values: vec![
247 fdf::NodePropertyValue::StringValue(
248 "rule_val".to_string(),
249 ),
250 fdf::NodePropertyValue::StringValue(
251 "rule_val_2".to_string(),
252 ),
253 ],
254 }],
255 properties: vec![fdf::NodeProperty2 {
256 key: "prop_key_0".to_string(),
257 value: fdf::NodePropertyValue::StringValue(
258 "prop_val_0".to_string(),
259 ),
260 }],
261 },
262 fdf::ParentSpec2 {
263 bind_rules: vec![
264 fdf::BindRule2 {
265 key: "0x0001".to_string(),
266 condition: fdf::Condition::Accept,
267 values: vec![
268 fdf::NodePropertyValue::IntValue(0x42),
269 fdf::NodePropertyValue::IntValue(0x123),
270 fdf::NodePropertyValue::IntValue(0x234),
271 ],
272 },
273 fdf::BindRule2 {
274 key: "0xdeadbeef".to_string(),
275 condition: fdf::Condition::Accept,
276 values: vec![fdf::NodePropertyValue::IntValue(
277 0xbeef,
278 )],
279 },
280 ],
281 properties: vec![
282 fdf::NodeProperty2 {
283 key: "prop_key_1".to_string(),
284 value: fdf::NodePropertyValue::EnumValue(
285 "prop_key_1.prop_val".to_string(),
286 ),
287 },
288 fdf::NodeProperty2 {
289 key: "prop_key_2".to_string(),
290 value: fdf::NodePropertyValue::IntValue(0x1),
291 },
292 fdf::NodeProperty2 {
293 key: "prop_key_3".to_string(),
294 value: fdf::NodePropertyValue::BoolValue(true),
295 },
296 ],
297 },
298 ]),
299 ..Default::default()
300 }),
301 matched_driver: Some(fdf::CompositeDriverMatch {
302 composite_driver: Some(fdf::CompositeDriverInfo {
303 driver_info: Some(fdf::DriverInfo {
304 url: Some("driver_url".to_string()),
305 ..Default::default()
306 }),
307 ..Default::default()
308 }),
309 parent_names: Some(vec![
310 "name_one".to_string(),
311 "name_two".to_string(),
312 ]),
313 primary_parent_index: Some(1),
314 ..Default::default()
315 }),
316 ..Default::default()
317 },
318 ],
319 iterator,
320 )
321 .await
322 .context("Failed to run driver info iterator server")?,
323 _ => {}
324 }
325 Ok(())
326 },
327 )
328 .await
329 .unwrap();
330
331 assert_eq!(
332 output,
333 r#"WARNING: This command is deprecated. Use `ffx driver composite list` or `ffx driver composite show` instead.
334Name : test_spec
335Driver : None
336Nodes : 1
337Node 0 : None
338 1 Bind Rules
339 [ 1/ 1] : Accept rule_key { "rule_val" }
340 1 Properties
341 [ 1/ 1] : Key prop_key Value "prop_val"
342
343Name : test_spec_with_driver
344Driver : driver_url
345Nodes : 2
346Node 0 : "name_one"
347 1 Bind Rules
348 [ 1/ 1] : Accept rule_key { "rule_val", "rule_val_2" }
349 1 Properties
350 [ 1/ 1] : Key prop_key_0 Value "prop_val_0"
351Node 1 : "name_two" (Primary)
352 2 Bind Rules
353 [ 1/ 2] : Accept 0x0001 { 0x000042, 0x000123, 0x000234 }
354 [ 2/ 2] : Accept 0xdeadbeef { 0x00beef }
355 3 Properties
356 [ 1/ 3] : Key prop_key_1 Value Enum(prop_key_1.prop_val)
357 [ 2/ 3] : Key prop_key_2 Value 0x000001
358 [ 3/ 3] : Key prop_key_3 Value true
359
360"#
361 );
362 }
363}