Skip to main content

driver_tools/subcommands/list/
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::ListCommand;
9use fidl_fuchsia_driver_development as fdd;
10use futures::join;
11use std::collections::HashSet;
12use std::io::Write;
13
14pub async fn list(
15    cmd: ListCommand,
16    writer: &mut dyn Write,
17    driver_development_proxy: fdd::ManagerProxy,
18) -> Result<()> {
19    if cmd.verbose {
20        writeln!(
21            writer,
22            "WARNING: The verbose flag is deprecated. Use `ffx driver show` instead."
23        )?;
24        return Ok(());
25    }
26
27    let empty: [String; 0] = [];
28    let driver_info = fuchsia_driver_dev::get_driver_info(&driver_development_proxy, &empty);
29
30    let driver_info = if cmd.loaded {
31        // Query devices and create a hash set of loaded drivers.
32        let device_info = fuchsia_driver_dev::get_device_info(
33            &driver_development_proxy,
34            &empty,
35            /* exact_match= */ false,
36        );
37
38        // Await the futures concurrently.
39        let (driver_info, device_info) = join!(driver_info, device_info);
40
41        let loaded_driver_set: HashSet<String> = HashSet::from_iter(
42            device_info?.into_iter().filter_map(|device_info| device_info.bound_driver_url),
43        );
44
45        // Filter the driver list by the hash set.
46        driver_info?
47            .into_iter()
48            .filter(|driver| {
49                let mut loaded = false;
50                if let Some(ref url) = driver.url {
51                    if loaded_driver_set.contains(url) {
52                        loaded = true
53                    }
54                }
55                loaded
56            })
57            .collect()
58    } else {
59        driver_info.await.context("Failed to get driver info")?
60    };
61
62    for driver in driver_info {
63        if let Some(name) = driver.name {
64            let url = driver.url.unwrap_or_default();
65            writeln!(writer, "{:<20}: {}", name, url)?;
66        } else {
67            let url = driver.url.unwrap_or_default();
68            writeln!(writer, "{}", url)?;
69        }
70    }
71    Ok(())
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use argh::FromArgs;
78    use fidl::endpoints::ServerEnd;
79    use fidl_fuchsia_driver_framework as fdf;
80    use fuchsia_async as fasync;
81    use futures::future::{Future, FutureExt};
82    use futures::stream::StreamExt;
83
84    /// Invokes `list` with `cmd` and runs a mock driver development server that
85    /// invokes `on_driver_development_request` whenever it receives a request.
86    /// The output of `list` that is normally written to its `writer` parameter
87    /// is returned.
88    async fn test_list<F, Fut>(cmd: ListCommand, on_driver_development_request: F) -> Result<String>
89    where
90        F: Fn(fdd::ManagerRequest) -> Fut + Send + Sync + 'static,
91        Fut: Future<Output = Result<()>> + Send + Sync,
92    {
93        let (driver_development_proxy, mut driver_development_requests) =
94            fidl::endpoints::create_proxy_and_stream::<fdd::ManagerMarker>();
95
96        // Run the command and mock driver development server.
97        let mut writer = Vec::new();
98        let request_handler_task = fasync::Task::spawn(async move {
99            while let Some(res) = driver_development_requests.next().await {
100                let request = res.context("Failed to get next request")?;
101                on_driver_development_request(request).await.context("Failed to handle request")?;
102            }
103            anyhow::bail!("Driver development request stream unexpectedly closed");
104        });
105        futures::select! {
106            res = request_handler_task.fuse() => {
107                res?;
108                anyhow::bail!("Request handler task unexpectedly finished");
109            }
110            res = list(cmd, &mut writer, driver_development_proxy).fuse() => res.context("List command failed")?,
111        }
112
113        String::from_utf8(writer).context("Failed to convert list output to a string")
114    }
115
116    async fn run_driver_info_iterator_server(
117        mut driver_infos: Vec<fdf::DriverInfo>,
118        iterator: ServerEnd<fdd::DriverInfoIteratorMarker>,
119    ) -> Result<()> {
120        let mut iterator = iterator.into_stream();
121        while let Some(res) = iterator.next().await {
122            let request = res.context("Failed to get request")?;
123            match request {
124                fdd::DriverInfoIteratorRequest::GetNext { responder } => {
125                    responder
126                        .send(&driver_infos)
127                        .context("Failed to send driver infos to responder")?;
128                    driver_infos.clear();
129                }
130            }
131        }
132        Ok(())
133    }
134
135    #[fasync::run_singlethreaded(test)]
136    async fn test_list_all() {
137        let cmd = ListCommand::from_args(&["list"], &[]).unwrap();
138
139        let output = test_list(cmd, |request: fdd::ManagerRequest| async move {
140            match request {
141                fdd::ManagerRequest::GetDriverInfo {
142                    driver_filter: _,
143                    iterator,
144                    control_handle: _,
145                } => run_driver_info_iterator_server(
146                    vec![fdf::DriverInfo {
147                        name: Some("foo".to_owned()),
148                        url: Some("fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned()),
149                        ..Default::default()
150                    }],
151                    iterator,
152                )
153                .await
154                .context("Failed to run driver info iterator server")?,
155                _ => {}
156            }
157            Ok(())
158        })
159        .await
160        .unwrap();
161
162        assert_eq!(
163            output,
164            "foo                 : fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm\n"
165        );
166    }
167
168    #[fasync::run_singlethreaded(test)]
169    async fn test_verbose_deprecated() {
170        let cmd = ListCommand::from_args(&["list"], &["--verbose"]).unwrap();
171
172        let output = test_list(cmd, |request: fdd::ManagerRequest| async move {
173            match request {
174                fdd::ManagerRequest::GetDriverInfo {
175                    driver_filter: _,
176                    iterator,
177                    control_handle: _,
178                } => run_driver_info_iterator_server(
179                    vec![fdf::DriverInfo {
180                        name: Some("foo".to_owned()),
181                        url: Some("fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned()),
182                        ..Default::default()
183                    }],
184                    iterator,
185                )
186                .await
187                .context("Failed to run driver info iterator server")?,
188                _ => {}
189            }
190            Ok(())
191        })
192        .await
193        .unwrap();
194
195        assert_eq!(
196            output,
197            "WARNING: The verbose flag is deprecated. Use `ffx driver show` instead.\n"
198        );
199    }
200}