1use anyhow::{Context, Result, anyhow, format_err};
6use flex_client::ProxyHasDomain;
7use {flex_fuchsia_driver_development as fdd, flex_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_deref();
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 service.domain().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: Vec<fdd::NodeInfo> =
74 result.context("FIDL call to get device info failed")?;
75 if device_info.is_empty() {
76 break 'outer;
77 }
78 info_result.append(&mut device_info)
79 }
80 }
81 Ok(info_result)
82}
83
84pub async fn get_driver_info(
86 service: &fdd::ManagerProxy,
87 driver_filter: &[String],
88) -> Result<Vec<fdf::DriverInfo>> {
89 let (iterator, iterator_server) =
90 service.domain().create_proxy::<fdd::DriverInfoIteratorMarker>();
91
92 service
93 .get_driver_info(driver_filter, iterator_server)
94 .context("FIDL call to get driver info failed")?;
95
96 let mut info_result = Vec::new();
97 'outer: loop {
98 let driver_info_futures = vec![
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 iterator.get_next(),
108 ];
109 let driver_info_result = futures::future::join_all(driver_info_futures).await;
110 for result in driver_info_result {
111 let mut driver_info: Vec<fdf::DriverInfo> =
112 result.context("FIDL call to get driver info failed")?;
113 if driver_info.is_empty() {
114 break 'outer;
115 }
116 info_result.append(&mut driver_info)
117 }
118 }
119 Ok(info_result)
120}
121
122pub async fn get_drivers_from_query(
123 query: &str,
124 driver_development_proxy: &fdd::ManagerProxy,
125) -> Result<Vec<fdf::DriverInfo>> {
126 let driver_filter = [query.to_string()];
129 let driver_info = get_driver_info(driver_development_proxy, &driver_filter).await;
130
131 match driver_info {
132 Ok(drivers) if !drivers.is_empty() => Ok(drivers),
133 _ => {
134 let empty: [String; 0] = [];
136 let all_drivers = get_driver_info(driver_development_proxy, &empty).await?;
137 Ok(all_drivers
138 .into_iter()
139 .filter(|driver| {
140 let url_match = driver.url.as_ref().map(|u| u.contains(query)).unwrap_or(false);
141 if url_match {
142 return true;
143 }
144 driver.name.as_ref().map(|n| n.contains(query)).unwrap_or(false)
145 })
146 .collect())
147 }
148 }
149}
150
151#[derive(Debug)]
152pub enum GetSingleDriverError {
153 NotFound(String),
154 Ambiguous { query: String, drivers: String },
155 UnderlyingError(anyhow::Error),
156}
157
158impl std::fmt::Display for GetSingleDriverError {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 match self {
161 GetSingleDriverError::NotFound(query) => {
162 write!(f, "No matching driver found for query {:?}.", query)
163 }
164 GetSingleDriverError::Ambiguous { query, drivers } => {
165 write!(
166 f,
167 "The query {:?} matches more than one driver:\n{}\n\nTo avoid ambiguity, use a more specific query.",
168 query, drivers
169 )
170 }
171 GetSingleDriverError::UnderlyingError(e) => write!(f, "{}", e),
172 }
173 }
174}
175
176impl std::error::Error for GetSingleDriverError {}
177
178impl From<anyhow::Error> for GetSingleDriverError {
179 fn from(e: anyhow::Error) -> Self {
180 GetSingleDriverError::UnderlyingError(e)
181 }
182}
183
184pub async fn get_single_driver_from_query(
185 query: &str,
186 driver_development_proxy: &fdd::ManagerProxy,
187) -> Result<fdf::DriverInfo, GetSingleDriverError> {
188 let mut filtered_drivers = get_drivers_from_query(query, driver_development_proxy)
189 .await
190 .map_err(anyhow::Error::from)?;
191
192 if filtered_drivers.len() > 1 {
193 let driver_names: Vec<String> = filtered_drivers
194 .iter()
195 .map(|d| {
196 d.url.as_ref().cloned().unwrap_or_else(|| {
197 d.name.as_ref().cloned().unwrap_or_else(|| "Unknown".to_string())
198 })
199 })
200 .collect();
201 let driver_names = driver_names.join("\n");
202 return Err(GetSingleDriverError::Ambiguous {
203 query: query.to_string(),
204 drivers: driver_names,
205 });
206 }
207
208 if filtered_drivers.is_empty() {
209 return Err(GetSingleDriverError::NotFound(query.to_string()));
210 }
211
212 Ok(filtered_drivers.remove(0))
213}
214
215pub async fn get_composite_info(
217 service: &fdd::ManagerProxy,
218) -> Result<Vec<fdd::CompositeNodeInfo>> {
219 let (iterator, iterator_server) =
220 service.domain().create_proxy::<fdd::CompositeInfoIteratorMarker>();
221
222 service
223 .get_composite_info(iterator_server)
224 .context("FIDL call to get composite info failed")?;
225
226 let mut info_result = Vec::new();
227 loop {
228 let mut info =
229 iterator.get_next().await.context("FIDL call to get composite info failed")?;
230 if info.is_empty() {
231 break;
232 }
233 info_result.append(&mut info)
234 }
235 Ok(info_result)
236}
237
238pub async fn get_driver_host_info(service: &fdd::ManagerProxy) -> Result<Vec<fdd::DriverHostInfo>> {
240 let (iterator, iterator_server) =
241 service.domain().create_proxy::<fdd::DriverHostInfoIteratorMarker>();
242
243 service
244 .get_driver_host_info(iterator_server)
245 .context("FIDL call to get driver host info failed")?;
246
247 let mut info_result = Vec::new();
248 loop {
249 let mut info: Vec<fdd::DriverHostInfo> =
250 iterator.get_next().await.context("FIDL call to get driver host info failed")?;
251 if info.is_empty() {
252 break;
253 }
254 info_result.append(&mut info)
255 }
256 Ok(info_result)
257}
258
259pub async fn get_composite_node_specs(
261 service: &fdd::ManagerProxy,
262 name_filter: Option<String>,
263) -> Result<Vec<fdf::CompositeInfo>> {
264 let (iterator, iterator_server) =
265 service.domain().create_proxy::<fdd::CompositeNodeSpecIteratorMarker>();
266
267 service
268 .get_composite_node_specs(name_filter.as_deref(), iterator_server)
269 .context("FIDL call to get node groups failed")?;
270
271 let mut info_result = Vec::new();
272 loop {
273 let mut node_groups: Vec<fdf::CompositeInfo> =
274 iterator.get_next().await.context("FIDL call to get node groups failed")?;
275 if node_groups.is_empty() {
276 break;
277 }
278 info_result.append(&mut node_groups)
279 }
280 Ok(info_result)
281}
282
283pub async fn get_driver_by_filter(
291 driver_filter: &str,
292 driver_development_proxy: &fdd::ManagerProxy,
293) -> Result<fdf::DriverInfo> {
294 let filter_list: [String; 1] = [driver_filter.to_string()];
295 let driver_list = get_driver_info(driver_development_proxy, &filter_list).await?;
296 if driver_list.len() != 1 {
297 return Err(anyhow!(
298 "There should be exactly one match for '{}'. Found {}.",
299 driver_filter,
300 driver_list.len()
301 ));
302 }
303 let mut driver_info: Option<fdf::DriverInfo> = None;
304
305 let driver = &driver_list[0];
307 if let Some(ref url) = driver.url {
308 if url == driver_filter {
309 driver_info = Some(driver.clone());
310 }
311 }
312 match driver_info {
313 Some(driver) => Ok(driver),
314 _ => Err(anyhow!("Did not find matching driver for: {}", driver_filter)),
315 }
316}
317
318pub async fn get_driver_by_device(
326 device_topo_path: &str,
327 driver_development_proxy: &fdd::ManagerProxy,
328) -> Result<fdf::DriverInfo> {
329 let device_filter: [String; 1] = [device_topo_path.to_string()];
330 let mut device_list =
331 get_device_info(driver_development_proxy, &device_filter, true).await?;
332 if device_list.len() != 1 {
333 let fuzzy_device_list = get_device_info(
334 driver_development_proxy,
335 &device_filter,
336 false,
337 )
338 .await?;
339 if fuzzy_device_list.is_empty() {
340 return Err(anyhow!("No devices matched the query: {}", device_topo_path));
341 } else if fuzzy_device_list.len() > 1 {
342 let mut builder = "Found multiple matches. Did you mean one of these?\n\n".to_string();
343 for item in fuzzy_device_list {
344 let device: Device = item.into();
345 builder = format!("{}{}\n", builder, device.get_full_name()?);
347 }
348 return Err(anyhow!(builder));
349 }
350 device_list = fuzzy_device_list;
351 }
352
353 let found_device = device_list.remove(0);
354 match found_device.bound_driver_url {
355 Some(ref driver_filter) => {
356 get_driver_by_filter(driver_filter, driver_development_proxy).await
357 }
358 _ => Err(anyhow!("Did not find driver for device {}", &device_topo_path)),
359 }
360}
361
362pub async fn get_devices_by_driver(
370 driver_filter: &str,
371 driver_development_proxy: &fdd::ManagerProxy,
372) -> Result<Vec<Device>> {
373 let driver_info_fut = get_driver_by_filter(driver_filter, driver_development_proxy);
374 let empty: [String; 0] = [];
375 let device_list_fut =
376 get_device_info(driver_development_proxy, &empty, false);
377
378 let (driver_info, device_list) = futures::join!(driver_info_fut, device_list_fut);
379 let (driver_info, device_list) = (driver_info?, device_list?);
380
381 let mut matches: Vec<Device> = Vec::new();
382 for device_item in device_list {
383 let device: Device = device_item.into();
384 if let (Some(bound_driver_url), Some(url)) = (&device.0.bound_driver_url, &driver_info.url)
385 {
386 if url == bound_driver_url {
387 matches.push(device);
388 }
389 }
390 }
391 Ok(matches)
392}