1use anyhow::{Context, Result, anyhow, format_err};
6use fidl_fuchsia_driver_development as fdd;
7use fidl_fuchsia_driver_framework as fdf;
8
9#[derive(Debug)]
10pub struct Device(pub fdd::NodeInfo);
11
12impl Device {
13 pub fn get_moniker(&self) -> Result<&str> {
15 let moniker = self.0.moniker.as_ref();
16 Ok(moniker.ok_or_else(|| format_err!("Missing moniker"))?)
17 }
18
19 pub fn get_full_name(&self) -> Result<&str> {
21 self.get_moniker()
22 }
23
24 pub fn extract_name(&self) -> Result<&str> {
28 let moniker = self.get_moniker()?;
29 let (_, name) = moniker.rsplit_once('.').unwrap_or(("", &moniker));
30 Ok(name)
31 }
32}
33
34impl std::convert::From<fdd::NodeInfo> for Device {
35 fn from(device_info: fdd::NodeInfo) -> Device {
36 Device(device_info)
37 }
38}
39
40pub async fn get_device_info(
42 service: &fdd::ManagerProxy,
43 device_filter: &[String],
44 exact_match: bool,
45) -> Result<Vec<fdd::NodeInfo>> {
46 let (iterator, iterator_server) =
47 fidl::endpoints::create_proxy::<fdd::NodeInfoIteratorMarker>();
48
49 service
50 .get_node_info(device_filter, iterator_server, exact_match)
51 .context("FIDL call to get device info failed")?;
52
53 let mut info_result = Vec::new();
54
55 'outer: loop {
56 let device_info_futures = vec![
58 iterator.get_next(),
59 iterator.get_next(),
60 iterator.get_next(),
61 iterator.get_next(),
62 iterator.get_next(),
63 iterator.get_next(),
64 iterator.get_next(),
65 iterator.get_next(),
66 iterator.get_next(),
67 iterator.get_next(),
68 iterator.get_next(),
69 iterator.get_next(),
70 ];
71 let device_info_result = futures::future::join_all(device_info_futures).await;
72 for result in device_info_result {
73 let mut device_info = result.context("FIDL call to get device info failed")?;
74 if device_info.len() == 0 {
75 break 'outer;
76 }
77 info_result.append(&mut device_info)
78 }
79 }
80 Ok(info_result)
81}
82
83pub async fn get_driver_info(
85 service: &fdd::ManagerProxy,
86 driver_filter: &[String],
87) -> Result<Vec<fdf::DriverInfo>> {
88 let (iterator, iterator_server) =
89 fidl::endpoints::create_proxy::<fdd::DriverInfoIteratorMarker>();
90
91 service
92 .get_driver_info(driver_filter, iterator_server)
93 .context("FIDL call to get driver info failed")?;
94
95 let mut info_result = Vec::new();
96 'outer: loop {
97 let driver_info_futures = vec![
99 iterator.get_next(),
100 iterator.get_next(),
101 iterator.get_next(),
102 iterator.get_next(),
103 iterator.get_next(),
104 iterator.get_next(),
105 iterator.get_next(),
106 iterator.get_next(),
107 ];
108 let driver_info_result = futures::future::join_all(driver_info_futures).await;
109 for result in driver_info_result {
110 let mut driver_info = result.context("FIDL call to get driver info failed")?;
111 if driver_info.len() == 0 {
112 break 'outer;
113 }
114 info_result.append(&mut driver_info)
115 }
116 }
117 Ok(info_result)
118}
119
120pub async fn get_drivers_from_query(
121 query: &str,
122 driver_development_proxy: &fdd::ManagerProxy,
123) -> Result<Vec<fdf::DriverInfo>> {
124 let driver_filter = [query.to_string()];
127 let driver_info = get_driver_info(driver_development_proxy, &driver_filter).await;
128
129 match driver_info {
130 Ok(drivers) if !drivers.is_empty() => Ok(drivers),
131 _ => {
132 let empty: [String; 0] = [];
134 let all_drivers = get_driver_info(driver_development_proxy, &empty).await?;
135 Ok(all_drivers
136 .into_iter()
137 .filter(|driver| {
138 let url_match = driver.url.as_ref().map(|u| u.contains(query)).unwrap_or(false);
139 if url_match {
140 return true;
141 }
142 driver.name.as_ref().map(|n| n.contains(query)).unwrap_or(false)
143 })
144 .collect())
145 }
146 }
147}
148
149#[derive(Debug)]
150pub enum GetSingleDriverError {
151 NotFound(String),
152 Ambiguous { query: String, drivers: String },
153 UnderlyingError(anyhow::Error),
154}
155
156impl std::fmt::Display for GetSingleDriverError {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 match self {
159 GetSingleDriverError::NotFound(query) => {
160 write!(f, "No matching driver found for query {:?}.", query)
161 }
162 GetSingleDriverError::Ambiguous { query, drivers } => {
163 write!(
164 f,
165 "The query {:?} matches more than one driver:\n{}\n\nTo avoid ambiguity, use a more specific query.",
166 query, drivers
167 )
168 }
169 GetSingleDriverError::UnderlyingError(e) => write!(f, "{}", e),
170 }
171 }
172}
173
174impl std::error::Error for GetSingleDriverError {}
175
176impl From<anyhow::Error> for GetSingleDriverError {
177 fn from(e: anyhow::Error) -> Self {
178 GetSingleDriverError::UnderlyingError(e)
179 }
180}
181
182pub async fn get_single_driver_from_query(
183 query: &str,
184 driver_development_proxy: &fdd::ManagerProxy,
185) -> Result<fdf::DriverInfo, GetSingleDriverError> {
186 let mut filtered_drivers = get_drivers_from_query(query, driver_development_proxy)
187 .await
188 .map_err(anyhow::Error::from)?;
189
190 if filtered_drivers.len() > 1 {
191 let driver_names: Vec<String> = filtered_drivers
192 .iter()
193 .map(|d| {
194 d.url.as_ref().cloned().unwrap_or_else(|| {
195 d.name.as_ref().cloned().unwrap_or_else(|| "Unknown".to_string())
196 })
197 })
198 .collect();
199 let driver_names = driver_names.join("\n");
200 return Err(GetSingleDriverError::Ambiguous {
201 query: query.to_string(),
202 drivers: driver_names,
203 });
204 }
205
206 if filtered_drivers.is_empty() {
207 return Err(GetSingleDriverError::NotFound(query.to_string()));
208 }
209
210 Ok(filtered_drivers.remove(0))
211}
212
213pub async fn get_composite_info(
215 service: &fdd::ManagerProxy,
216) -> Result<Vec<fdd::CompositeNodeInfo>> {
217 let (iterator, iterator_server) =
218 fidl::endpoints::create_proxy::<fdd::CompositeInfoIteratorMarker>();
219
220 service
221 .get_composite_info(iterator_server)
222 .context("FIDL call to get composite info failed")?;
223
224 let mut info_result = Vec::new();
225 loop {
226 let mut info =
227 iterator.get_next().await.context("FIDL call to get composite info failed")?;
228 if info.is_empty() {
229 break;
230 }
231 info_result.append(&mut info)
232 }
233 Ok(info_result)
234}
235
236pub async fn get_driver_host_info(service: &fdd::ManagerProxy) -> Result<Vec<fdd::DriverHostInfo>> {
238 let (iterator, iterator_server) =
239 fidl::endpoints::create_proxy::<fdd::DriverHostInfoIteratorMarker>();
240
241 service
242 .get_driver_host_info(iterator_server)
243 .context("FIDL call to get driver host info failed")?;
244
245 let mut info_result = Vec::new();
246 loop {
247 let mut info =
248 iterator.get_next().await.context("FIDL call to get driver host info failed")?;
249 if info.is_empty() {
250 break;
251 }
252 info_result.append(&mut info)
253 }
254 Ok(info_result)
255}
256
257pub async fn get_composite_node_specs(
259 service: &fdd::ManagerProxy,
260 name_filter: Option<String>,
261) -> Result<Vec<fdf::CompositeInfo>> {
262 let (iterator, iterator_server) =
263 fidl::endpoints::create_proxy::<fdd::CompositeNodeSpecIteratorMarker>();
264
265 service
266 .get_composite_node_specs(name_filter.as_deref(), iterator_server)
267 .context("FIDL call to get node groups failed")?;
268
269 let mut info_result = Vec::new();
270 loop {
271 let mut node_groups =
272 iterator.get_next().await.context("FIDL call to get node groups failed")?;
273 if node_groups.is_empty() {
274 break;
275 }
276 info_result.append(&mut node_groups)
277 }
278 Ok(info_result)
279}
280
281pub async fn get_driver_by_filter(
289 driver_filter: &String,
290 driver_development_proxy: &fdd::ManagerProxy,
291) -> Result<fdf::DriverInfo> {
292 let filter_list: [String; 1] = [driver_filter.to_string()];
293 let driver_list = get_driver_info(&driver_development_proxy, &filter_list).await?;
294 if driver_list.len() != 1 {
295 return Err(anyhow!(
296 "There should be exactly one match for '{}'. Found {}.",
297 driver_filter,
298 driver_list.len()
299 ));
300 }
301 let mut driver_info: Option<fdf::DriverInfo> = None;
302
303 let driver = &driver_list[0];
305 if let Some(ref url) = driver.url {
306 if url == driver_filter {
307 driver_info = Some(driver.clone());
308 }
309 }
310 match driver_info {
311 Some(driver) => Ok(driver),
312 _ => Err(anyhow!("Did not find matching driver for: {}", driver_filter)),
313 }
314}
315
316pub async fn get_driver_by_device(
324 device_topo_path: &String,
325 driver_development_proxy: &fdd::ManagerProxy,
326) -> Result<fdf::DriverInfo> {
327 let device_filter: [String; 1] = [device_topo_path.to_string()];
328 let mut device_list =
329 get_device_info(&driver_development_proxy, &device_filter, true).await?;
330 if device_list.len() != 1 {
331 let fuzzy_device_list = get_device_info(
332 &driver_development_proxy,
333 &device_filter,
334 false,
335 )
336 .await?;
337 if fuzzy_device_list.len() == 0 {
338 return Err(anyhow!("No devices matched the query: {}", device_topo_path));
339 } else if fuzzy_device_list.len() > 1 {
340 let mut builder = "Found multiple matches. Did you mean one of these?\n\n".to_string();
341 for item in fuzzy_device_list {
342 let device: Device = item.into();
343 builder = format!("{}{}\n", builder, device.get_full_name()?);
345 }
346 return Err(anyhow!(builder));
347 }
348 device_list = fuzzy_device_list;
349 }
350
351 let found_device = device_list.remove(0);
352 match found_device.bound_driver_url {
353 Some(ref driver_filter) => {
354 get_driver_by_filter(&driver_filter, &driver_development_proxy).await
355 }
356 _ => Err(anyhow!("Did not find driver for device {}", &device_topo_path)),
357 }
358}
359
360pub async fn get_devices_by_driver(
368 driver_filter: &String,
369 driver_development_proxy: &fdd::ManagerProxy,
370) -> Result<Vec<Device>> {
371 let driver_info = get_driver_by_filter(driver_filter, &driver_development_proxy);
372 let empty: [String; 0] = [];
373 let device_list =
374 get_device_info(&driver_development_proxy, &empty, false);
375
376 let (driver_info, device_list) = futures::join!(driver_info, device_list);
377 let (driver_info, device_list) = (driver_info?, device_list?);
378
379 let mut matches: Vec<Device> = Vec::new();
380 for device_item in device_list.into_iter() {
381 let device: Device = device_item.into();
382 if let (Some(bound_driver_url), Some(url)) = (&device.0.bound_driver_url, &driver_info.url)
383 {
384 if &url == &bound_driver_url {
385 matches.push(device);
386 }
387 }
388 }
389 Ok(matches)
390}