driver_tools/subcommands/list_composites/
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 anyhow::{Context, Result};
8use args::ListCompositesCommand;
9use std::io::Write;
10use {fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_framework as fdf};
11
12pub async fn list_composites(
13    cmd: ListCompositesCommand,
14    writer: &mut dyn Write,
15    proxy: fdd::ManagerProxy,
16) -> Result<()> {
17    let (iterator, iterator_server) =
18        fidl::endpoints::create_proxy::<fdd::CompositeInfoIteratorMarker>();
19    proxy.get_composite_info(iterator_server).context("GetCompositeInfo() failed")?;
20
21    loop {
22        let composite_list =
23            iterator.get_next().await.context("CompositeInfoIterator GetNext() failed")?;
24
25        if composite_list.is_empty() {
26            break;
27        }
28
29        for composite_node in composite_list {
30            match composite_node.composite {
31                Some(fdd::CompositeInfo::Composite(info)) => {
32                    write_composite(
33                        writer,
34                        info,
35                        composite_node.parent_topological_paths.unwrap(),
36                        composite_node.topological_path,
37                        cmd.verbose,
38                    )?;
39                }
40                _ => {}
41            }
42        }
43    }
44
45    Ok(())
46}
47
48fn write_composite(
49    writer: &mut dyn Write,
50    composite: fdf::CompositeInfo,
51    parent_topological_paths: Vec<Option<String>>,
52    topological_path: Option<String>,
53    verbose: bool,
54) -> Result<()> {
55    let spec = composite.spec.unwrap_or_default();
56    let driver_match = composite.matched_driver.unwrap_or_default();
57    if !verbose {
58        writeln!(writer, "{}", spec.name.unwrap_or_else(|| "".to_string()))?;
59        return Ok(());
60    }
61
62    writeln!(writer, "{0: <9}: {1}", "Name", spec.name.unwrap_or_else(|| "".to_string()))?;
63    writeln!(
64        writer,
65        "{0: <9}: {1}",
66        "Driver",
67        driver_match
68            .composite_driver
69            .and_then(|composite_driver| composite_driver.driver_info)
70            .and_then(|driver_info| driver_info.url)
71            .unwrap_or_else(|| "N/A".to_string())
72    )?;
73    writeln!(
74        writer,
75        "{0: <9}: {1}",
76        "Device",
77        topological_path.unwrap_or_else(|| "N/A".to_string())
78    )?;
79
80    write_parent_nodes_info(
81        writer,
82        driver_match.primary_parent_index,
83        driver_match.parent_names.unwrap_or_default(),
84        parent_topological_paths,
85    )?;
86
87    writeln!(writer)?;
88    Ok(())
89}
90
91fn write_parent_nodes_info(
92    writer: &mut dyn Write,
93    primary_index: Option<u32>,
94    parent_names: Vec<String>,
95    parent_paths: Vec<Option<String>>,
96) -> Result<()> {
97    writeln!(writer, "{0: <9}: {1}", "Parents", parent_names.len())?;
98    for (i, parent_name) in parent_names.into_iter().enumerate() {
99        let primary_tag = if primary_index == Some(i as u32) { "(Primary)" } else { "" };
100        writeln!(writer, "{0: <1} {1} : {2} {3}", "Parent", i, parent_name, primary_tag)?;
101
102        writeln!(
103            writer,
104            "   {0: <1} : {1}",
105            "Device",
106            parent_paths[i].clone().unwrap_or_else(|| "Unbound".to_string())
107        )?;
108    }
109    Ok(())
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    use fuchsia_async as fasync;
116    use std::io::Error;
117
118    pub struct TestWriteBuffer {
119        pub content: String,
120    }
121
122    impl Write for TestWriteBuffer {
123        fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
124            self.content.push_str(std::str::from_utf8(buf).unwrap());
125            Ok(buf.len())
126        }
127
128        fn flush(&mut self) -> Result<(), Error> {
129            Ok(())
130        }
131    }
132
133    #[fasync::run_singlethreaded(test)]
134    async fn test_composite_verbose() {
135        let test_composite = fdf::CompositeInfo {
136            spec: Some(fdf::CompositeNodeSpec {
137                name: Some("composite_dev".to_string()),
138                ..Default::default()
139            }),
140            matched_driver: Some(fdf::CompositeDriverMatch {
141                composite_driver: Some(fdf::CompositeDriverInfo {
142                    driver_info: Some(fdf::DriverInfo {
143                        url: Some("fuchsia-boot:///#meta/waxwing.cm".to_string()),
144                        ..Default::default()
145                    }),
146                    ..Default::default()
147                }),
148                parent_names: Some(vec!["sysmem".to_string(), "acpi".to_string()]),
149                primary_parent_index: Some(1),
150                ..Default::default()
151            }),
152            ..Default::default()
153        };
154
155        let mut test_write_buffer = TestWriteBuffer { content: "".to_string() };
156        write_composite(
157            &mut test_write_buffer,
158            test_composite,
159            vec![Some("path/sysmem_dev".to_string()), Some("path/acpi_dev".to_string())],
160            Some("dev/sys/composite_dev".to_string()),
161            true,
162        )
163        .unwrap();
164        assert_eq!(
165            include_str!("../../../tests/golden/list_composites_verbose"),
166            test_write_buffer.content
167        );
168    }
169
170    #[fasync::run_singlethreaded(test)]
171    async fn test_composite_verbose_empty_fields() {
172        let test_composite = fdf::CompositeInfo {
173            spec: Some(fdf::CompositeNodeSpec {
174                name: Some("composite_dev".to_string()),
175                ..Default::default()
176            }),
177            matched_driver: Some(fdf::CompositeDriverMatch {
178                composite_driver: Some(fdf::CompositeDriverInfo {
179                    composite_name: Some("composite_name".to_string()),
180                    driver_info: Some(fdf::DriverInfo {
181                        url: Some("fuchsia-boot:///#meta/waxwing.cm".to_string()),
182                        ..Default::default()
183                    }),
184                    ..Default::default()
185                }),
186                parent_names: Some(vec!["sysmem".to_string(), "acpi".to_string()]),
187                primary_parent_index: Some(1),
188                ..Default::default()
189            }),
190            ..Default::default()
191        };
192
193        let mut test_write_buffer = TestWriteBuffer { content: "".to_string() };
194        write_composite(
195            &mut test_write_buffer,
196            test_composite,
197            vec![None, None],
198            Some("dev/sys/composite_dev".to_string()),
199            true,
200        )
201        .unwrap();
202        println!("{}", test_write_buffer.content);
203
204        assert_eq!(
205            include_str!("../../../tests/golden/list_composites_verbose_empty_fields"),
206            test_write_buffer.content
207        );
208    }
209}