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 futures::future::{Future, FutureExt};
82    use futures::stream::StreamExt;
83    #[cfg(feature = "fdomain")]
84    use std::sync::Arc;
85    use {flex_fuchsia_driver_framework as fdf, fuchsia_async as fasync};
86
87    /// Invokes `list` with `cmd` and runs a mock driver development server that
88    /// invokes `on_driver_development_request` whenever it receives a request.
89    /// The output of `list` that is normally written to its `writer` parameter
90    /// is returned.
91    async fn test_list<F, Fut>(
92        #[cfg(feature = "fdomain")] client: Arc<flex_client::Client>,
93        cmd: ListCommand,
94        on_driver_development_request: F,
95    ) -> Result<String>
96    where
97        F: Fn(fdd::ManagerRequest) -> Fut + Send + Sync + 'static,
98        Fut: Future<Output = Result<()>> + Send + Sync,
99    {
100        #[cfg(not(feature = "fdomain"))]
101        let client = flex_client::fidl::ZirconClient;
102        let (driver_development_proxy, mut driver_development_requests) =
103            client.create_proxy_and_stream::<fdd::ManagerMarker>();
104
105        // Run the command and mock driver development server.
106        let mut writer = Vec::new();
107        let request_handler_task = fasync::Task::spawn(async move {
108            while let Some(res) = driver_development_requests.next().await {
109                let request = res.unwrap();
110                on_driver_development_request(request).await.context("Failed to handle request")?;
111            }
112            anyhow::bail!("Driver development request stream unexpectedly closed");
113        });
114        futures::select! {
115            res = request_handler_task.fuse() => {
116                res?;
117                anyhow::bail!("Request handler task unexpectedly finished");
118            }
119            res = list(cmd, &mut writer, driver_development_proxy).fuse() => res.context("List command failed")?,
120        }
121
122        String::from_utf8(writer).context("Failed to convert list output to a string")
123    }
124
125    async fn run_driver_info_iterator_server(
126        mut driver_infos: Vec<fdf::DriverInfo>,
127        iterator: ServerEnd<fdd::DriverInfoIteratorMarker>,
128    ) -> Result<()> {
129        let mut iterator = iterator.into_stream();
130        while let Some(res) = iterator.next().await {
131            let request = res.unwrap();
132            match request {
133                fdd::DriverInfoIteratorRequest::GetNext { responder } => {
134                    responder.send(&driver_infos).unwrap();
135                    driver_infos.clear();
136                }
137            }
138        }
139        Ok(())
140    }
141
142    #[fasync::run_singlethreaded(test)]
143    async fn test_list_all() {
144        #[cfg(feature = "fdomain")]
145        let client = fdomain_local::local_client_empty();
146        let cmd = ListCommand::from_args(&["list"], &[]).unwrap();
147
148        let output = test_list(
149            #[cfg(feature = "fdomain")]
150            client,
151            cmd,
152            |request: fdd::ManagerRequest| async move {
153                match request {
154                    fdd::ManagerRequest::GetDriverInfo {
155                        driver_filter: _,
156                        iterator,
157                        control_handle: _,
158                    } => run_driver_info_iterator_server(
159                        vec![fdf::DriverInfo {
160                            name: Some("foo".to_owned()),
161                            url: Some(
162                                "fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned(),
163                            ),
164                            ..Default::default()
165                        }],
166                        iterator,
167                    )
168                    .await
169                    .context("Failed to run driver info iterator server")?,
170                    _ => {}
171                }
172                Ok(())
173            },
174        )
175        .await
176        .unwrap();
177
178        assert_eq!(
179            output,
180            "foo                 : fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm\n"
181        );
182    }
183
184    #[fasync::run_singlethreaded(test)]
185    async fn test_verbose_deprecated() {
186        #[cfg(feature = "fdomain")]
187        let client = fdomain_local::local_client_empty();
188        let cmd = ListCommand::from_args(&["list"], &["--verbose"]).unwrap();
189
190        let output = test_list(
191            #[cfg(feature = "fdomain")]
192            Arc::clone(&client),
193            cmd,
194            |request: fdd::ManagerRequest| async move {
195                match request {
196                    fdd::ManagerRequest::GetDriverInfo {
197                        driver_filter: _,
198                        iterator,
199                        control_handle: _,
200                    } => run_driver_info_iterator_server(
201                        vec![fdf::DriverInfo {
202                            name: Some("foo".to_owned()),
203                            url: Some(
204                                "fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned(),
205                            ),
206                            ..Default::default()
207                        }],
208                        iterator,
209                    )
210                    .await
211                    .context("Failed to run driver info iterator server")?,
212                    _ => {}
213                }
214                Ok(())
215            },
216        )
217        .await
218        .unwrap();
219
220        assert_eq!(
221            output,
222            "WARNING: The verbose flag is deprecated. Use `ffx driver show` instead.\n"
223        );
224    }
225}