blob: 24af75d16e0a155f34fa4943cae7beaf65c99794 [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::DiagnosticsProvider, Command, ListCommand},
types::Error,
};
use cm_rust::SourceName;
use component_debug::realm::*;
use fidl_fuchsia_sys2 as fsys2;
use lazy_static::lazy_static;
use moniker::{Moniker, MonikerBase};
use regex::Regex;
lazy_static! {
static ref EXPECTED_PROTOCOL_RE: Regex =
Regex::new(r".*fuchsia\.diagnostics\..*ArchiveAccessor$").unwrap();
}
/// Returns the selectors for a component whose url contains the `manifest` string.
pub async fn get_selectors_for_manifest<P: DiagnosticsProvider>(
manifest: &Option<String>,
tree_selectors: &Vec<String>,
accessor: &Option<String>,
provider: &P,
) -> Result<Vec<String>, Error> {
let Some(manifest) = manifest.as_ref() else {
return Ok(tree_selectors.clone());
};
let list_command = ListCommand {
manifest: Some(manifest.clone()),
with_url: false,
accessor: accessor.clone(),
};
let monikers = list_command
.execute(provider)
.await?
.into_inner()
.into_iter()
.map(|item| item.into_moniker())
.collect::<Vec<_>>();
if monikers.is_empty() {
Err(Error::ManifestNotFound(manifest.clone()))
} else if tree_selectors.is_empty() {
Ok(monikers.into_iter().map(|moniker| format!("{}:root", moniker)).collect())
} else {
Ok(monikers
.into_iter()
.flat_map(|moniker| {
tree_selectors
.iter()
.map(move |tree_selector| format!("{}:{}", moniker, tree_selector))
})
.collect())
}
}
/// Expand selectors.
pub fn expand_selectors(selectors: Vec<String>) -> Result<Vec<String>, Error> {
let mut result = vec![];
for selector in selectors {
match selectors::tokenize_string(&selector, selectors::SELECTOR_DELIMITER) {
Ok(tokens) => {
if tokens.len() > 1 {
result.push(selector);
} else if tokens.len() == 1 {
result.push(format!("{}:*", selector));
} else {
return Err(Error::InvalidArguments(format!(
"Iquery selectors cannot be empty strings: {:?}",
selector
)));
}
}
Err(e) => {
return Err(Error::InvalidArguments(format!(
"Tokenizing a provided selector failed. Error: {:?} Selector: {:?}",
e, selector
)));
}
}
}
Ok(result)
}
/// Helper method to normalize a moniker into its canonical string form. Returns
/// the input moniker unchanged if it cannot be parsed.
pub fn normalize_moniker(moniker: &str) -> String {
Moniker::parse_str(moniker).map_or(String::from(moniker), |m| m.to_string())
}
/// Get all the exposed `ArchiveAccessor` from any child component which
/// directly exposes them or places them in its outgoing directory.
pub async fn get_accessor_selectors(
realm_query: &fsys2::RealmQueryProxy,
) -> Result<Vec<String>, Error> {
let mut result = vec![];
let instances = get_all_instances(realm_query).await?;
for instance in instances {
match get_resolved_declaration(&instance.moniker, realm_query).await {
Ok(decl) => {
for capability in decl.capabilities {
let capability_name = capability.name().to_string();
if !EXPECTED_PROTOCOL_RE.is_match(&capability_name) {
continue;
}
// Skip .host accessors.
if capability_name.contains(".host") {
continue;
}
if decl.exposes.iter().any(|expose| expose.source_name() == capability.name()) {
let moniker_str = instance.moniker.to_string();
let moniker = selectors::sanitize_moniker_for_selectors(&moniker_str);
result.push(format!("{moniker}:expose:{capability_name}"));
}
}
}
Err(GetDeclarationError::InstanceNotFound(_))
| Err(GetDeclarationError::InstanceNotResolved(_)) => continue,
Err(err) => return Err(err.into()),
}
}
result.sort();
Ok(result)
}
#[cfg(test)]
mod test {
use {
super::*, assert_matches::assert_matches, iquery_test_support::MockRealmQuery,
std::sync::Arc,
};
#[fuchsia::test]
async fn test_get_accessors() {
let fake_realm_query = Arc::new(MockRealmQuery::default());
let realm_query = Arc::clone(&fake_realm_query).get_proxy().await;
let res = get_accessor_selectors(&realm_query).await;
assert_matches!(res, Ok(_));
assert_eq!(
res.unwrap(),
vec![
String::from("example/component:expose:fuchsia.diagnostics.ArchiveAccessor"),
String::from(
"foo/bar/thing\\:instance:expose:fuchsia.diagnostics.FeedbackArchiveAccessor"
),
String::from("foo/component:expose:fuchsia.diagnostics.FeedbackArchiveAccessor"),
]
);
}
}