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