blob: dfbefed1ccc21601778aeddd404afc22765eea56 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{commands::types::*, types::Error},
argh::FromArgs,
async_trait::async_trait,
fuchsia_zircon::DurationNum,
futures::future::join_all,
futures::stream::StreamExt,
glob,
lazy_static::lazy_static,
regex::Regex,
std::{collections::BTreeSet, path::PathBuf},
};
lazy_static! {
static ref EXPECTED_FILE_RE: &'static str = r"fuchsia\.diagnostics\..*ArchiveAccessor$";
static ref READDIR_TIMEOUT_SECONDS: i64 = 15;
}
/// Lists all ArchiveAccessor files under the provided paths. If no paths are provided, it'll list
/// under the current directory. At the moment v2 components cannot be seen through the filesystem.
/// Therefore this only outputs ArchiveAccessors exposed by v1 components.
#[derive(Default, FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "list-accessors")]
pub struct ListAccessorsCommand {
#[argh(positional)]
/// glob paths from where to list files.
pub paths: Vec<String>,
}
#[async_trait]
impl Command for ListAccessorsCommand {
type Result = Vec<String>;
async fn execute(&self) -> Result<Vec<String>, Error> {
let paths = if self.paths.is_empty() {
let path = std::env::current_dir()
.map_err(|e| Error::io_error("Failed to get current dir", e.into()))?;
vec![path.to_string_lossy().to_string()]
} else {
self.paths.clone()
};
let mut result = BTreeSet::new();
for query_path in paths {
let path_futs = glob::glob(&query_path)
.map_err(|e| Error::ParsePath(query_path.clone(), e.into()))?
.map(|path_result| match path_result {
Err(e) => Err(Error::ParsePath(query_path.clone(), e.into())),
Ok(path) => Ok(all_accessors(path.to_string_lossy().to_string())),
})
.collect::<Result<Vec<_>, _>>()?;
for paths_result in join_all(path_futs).await {
let paths =
paths_result.map_err(|e| Error::ListAccessors(query_path.clone(), e.into()))?;
result.extend(paths.into_iter());
}
}
Ok(result.into_iter().collect::<Vec<_>>())
}
}
async fn all_accessors(root: impl AsRef<str>) -> Result<Vec<String>, Error> {
let dir_proxy =
io_util::open_directory_in_namespace(root.as_ref(), io_util::OPEN_RIGHT_READABLE)
.map_err(|e| Error::io_error(format!("Open dir {}", root.as_ref()), e))?;
let expected_file_re = Regex::new(&EXPECTED_FILE_RE).unwrap();
let paths = files_async::readdir_recursive(&dir_proxy, Some(READDIR_TIMEOUT_SECONDS.seconds()))
.filter_map(|result| async {
match result {
Err(err) => {
eprintln!("{}", err);
None
}
Ok(entry) => {
if expected_file_re.is_match(&entry.name) {
let mut path = PathBuf::from(root.as_ref());
path.push(&entry.name);
Some(path.to_string_lossy().to_string())
} else {
None
}
}
}
})
.collect::<Vec<String>>()
.await;
Ok(paths)
}