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