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