use crate::commands::types::*;
use crate::commands::utils;
use crate::types::Error;
use argh::{ArgsInfo, FromArgs};
use component_debug::realm::Instance;
use diagnostics_data::{Inspect, InspectData};
use fidl_fuchsia_diagnostics::Selector;
use fidl_fuchsia_sys2 as fsys;
use serde::{Serialize, Serializer};
use std::cmp::Ordering;
use std::collections::BTreeSet;
use std::fmt;
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Serialize)]
pub struct MonikerWithUrl {
pub moniker: String,
pub component_url: String,
}
#[derive(Debug, Eq, PartialEq)]
pub enum ListResultItem {
Moniker(String),
MonikerWithUrl(MonikerWithUrl),
}
impl ListResultItem {
pub fn into_moniker(self) -> String {
let moniker = match self {
Self::Moniker(moniker) => moniker,
Self::MonikerWithUrl(MonikerWithUrl { moniker, .. }) => moniker,
};
selectors::sanitize_moniker_for_selectors(&moniker)
}
}
impl Ord for ListResultItem {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(ListResultItem::Moniker(moniker), ListResultItem::Moniker(other_moniker))
| (
ListResultItem::MonikerWithUrl(MonikerWithUrl { moniker, .. }),
ListResultItem::MonikerWithUrl(MonikerWithUrl { moniker: other_moniker, .. }),
) => moniker.cmp(other_moniker),
_ => unreachable!("all lists must contain variants of the same type"),
}
}
}
impl PartialOrd for ListResultItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Serialize for ListResultItem {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Moniker(string) => serializer.serialize_str(string),
Self::MonikerWithUrl(data) => data.serialize(serializer),
}
}
}
#[derive(Serialize)]
pub struct ListResult(Vec<ListResultItem>);
impl ListResult {
pub(crate) fn into_inner(self) -> Vec<ListResultItem> {
self.0
}
}
impl fmt::Display for ListResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for item in self.0.iter() {
match item {
ListResultItem::Moniker(moniker) => {
writeln!(f, "{}", selectors::sanitize_moniker_for_selectors(moniker))?
}
ListResultItem::MonikerWithUrl(MonikerWithUrl { component_url, moniker }) => {
writeln!(f, "{}:", selectors::sanitize_moniker_for_selectors(moniker))?;
writeln!(f, " {}", component_url)?;
}
}
}
Ok(())
}
}
fn components_from_inspect_data(
inspect_data: Vec<InspectData>,
) -> impl Iterator<Item = ListResultItem> {
inspect_data.into_iter().map(|value| {
ListResultItem::MonikerWithUrl(MonikerWithUrl {
moniker: value.moniker.to_string(),
component_url: value.metadata.component_url.into(),
})
})
}
pub fn list_response_items(
manifest: Option<&str>,
with_url: bool,
components: impl Iterator<Item = ListResultItem>,
) -> Vec<ListResultItem> {
components
.filter(|result| match manifest {
None => true,
Some(manifest) => match result {
ListResultItem::Moniker(_) => true,
ListResultItem::MonikerWithUrl(url) => url.component_url.contains(manifest),
},
})
.map(|result| {
if with_url {
result
} else {
match result {
ListResultItem::Moniker(_) => result,
ListResultItem::MonikerWithUrl(val) => ListResultItem::Moniker(val.moniker),
}
}
})
.collect::<BTreeSet<_>>()
.into_iter()
.collect::<Vec<_>>()
}
#[derive(Default, ArgsInfo, FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "list")]
pub struct ListCommand {
#[argh(option)]
pub manifest: Option<String>,
#[argh(option)]
pub component: Option<String>,
#[argh(switch)]
pub with_url: bool,
#[argh(option)]
pub accessor: Option<String>,
}
impl Command for ListCommand {
type Result = ListResult;
async fn execute<P: DiagnosticsProvider>(self, provider: &P) -> Result<Self::Result, Error> {
let (inspect, manifest) = if self.manifest.is_some() {
eprintln!(
"WARNING: --manifest is DEPRECATED and will be removed soon. Please use --component only."
);
if self.component.is_some() {
eprintln!("WARNING: --component ignored due to usage of deprecated --manifest. Please use --component only.")
}
let inspect =
provider.snapshot::<Inspect>(self.accessor.as_deref(), std::iter::empty()).await?;
(inspect, self.manifest)
} else {
let mut selectors = if let Some(query) = self.component {
let instances = find_components(provider.realm_query(), &query).await?;
instances_to_root_selectors(instances)?
} else {
vec![]
};
utils::ensure_tree_field_is_set(&mut selectors, None)?;
let inspect = provider
.snapshot::<Inspect>(self.accessor.as_deref(), selectors.into_iter())
.await?;
(inspect, None)
};
let components = components_from_inspect_data(inspect);
let results = list_response_items(manifest.as_deref(), self.with_url, components);
Ok(ListResult(results))
}
}
async fn find_components(
realm_proxy: &fsys::RealmQueryProxy,
query: &str,
) -> Result<Vec<Instance>, Error> {
component_debug::query::get_instances_from_query(query, realm_proxy)
.await
.map_err(Error::FuzzyMatchRealmQuery)
}
fn instances_to_root_selectors(instances: Vec<Instance>) -> Result<Vec<Selector>, Error> {
instances
.into_iter()
.map(|instance| instance.moniker)
.map(|moniker| {
let selector_str = format!("{moniker}:root");
selectors::parse_verbose(&selector_str).map_err(Error::PartialSelectorHint)
})
.collect::<Result<Vec<Selector>, Error>>()
}
#[cfg(test)]
mod tests {
use super::*;
use diagnostics_data::{InspectDataBuilder, InspectHandleName, Timestamp};
#[fuchsia::test]
fn components_from_inspect_data_uses_diagnostics_ready() {
let inspect_data = vec![
InspectDataBuilder::new(
"some_moniker".try_into().unwrap(),
"fake-url",
Timestamp::from_nanos(123456789800i64),
)
.with_name(InspectHandleName::filename("fake-file"))
.build(),
InspectDataBuilder::new(
"other_moniker".try_into().unwrap(),
"other-fake-url",
Timestamp::from_nanos(123456789900i64),
)
.with_name(InspectHandleName::filename("fake-file"))
.build(),
InspectDataBuilder::new(
"some_moniker".try_into().unwrap(),
"fake-url",
Timestamp::from_nanos(123456789910i64),
)
.with_name(InspectHandleName::filename("fake-file"))
.build(),
InspectDataBuilder::new(
"different_moniker".try_into().unwrap(),
"different-fake-url",
Timestamp::from_nanos(123456790990i64),
)
.with_name(InspectHandleName::filename("fake-file"))
.build(),
];
let components = components_from_inspect_data(inspect_data).collect::<Vec<_>>();
assert_eq!(components.len(), 4);
}
}