driver_tools/subcommands/list_composites/
mod.rs1pub 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}