Skip to main content

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 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    /// Invokes `list_composite_node_specs` with `cmd` and runs a mock driver development server that
137    /// invokes `on_driver_development_request` whenever it receives a request.
138    /// The output of `list_composite_node_specs` that is normally written to its `writer` parameter
139    /// is returned.
140    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        // Run the command and mock driver development server.
155        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}