driver_tools/subcommands/list_composite_node_specs/
mod.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub 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    /// Invokes `list_composite_node_specs` with `cmd` and runs a mock driver development server that
113    /// invokes `on_driver_development_request` whenever it receives a request.
114    /// The output of `list_composite_node_specs` that is normally written to its `writer` parameter
115    /// is returned.
116    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        // Run the command and mock driver development server.
128        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}