1use anyhow::{Error, bail};
6use diagnostics_reader::{ArchiveReader, InspectArchiveReader, RetryConfig};
7use log::warn;
8
9const INSPECT_PREFIX: &str = "INSPECT:";
11
12pub struct InspectFetcher {
14 reader: Option<InspectArchiveReader>,
17}
18
19impl std::fmt::Debug for InspectFetcher {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.debug_struct("InspectFetcher").field("reader", &"opaque-ArchiveReader").finish()
22 }
23}
24
25impl InspectFetcher {
26 pub fn create(service_path: &str, selectors: Vec<String>) -> Result<InspectFetcher, Error> {
32 if selectors.is_empty() {
33 return Ok(InspectFetcher { reader: None });
34 }
35 let proxy = match fuchsia_component::client::connect_to_protocol_at_path::<
36 fidl_fuchsia_diagnostics::ArchiveAccessorMarker,
37 >(service_path)
38 {
39 Ok(proxy) => proxy,
40 Err(e) => bail!("Failed to connect to Inspect reader: {}", e),
41 };
42 let mut reader = ArchiveReader::inspect();
43 reader
44 .with_archive(proxy)
45 .retry(RetryConfig::never())
46 .add_selectors(Self::process_selectors(selectors)?.into_iter());
47 Ok(InspectFetcher { reader: Some(reader) })
48 }
49
50 pub async fn fetch(&mut self) -> Result<String, Error> {
53 match &self.reader {
54 None => Ok("[]".to_string()),
55 Some(reader) => {
56 Ok(reader.snapshot_raw::<serde_json::Value>().await?.to_string())
58 }
59 }
60 }
61
62 fn process_selectors(selectors: Vec<String>) -> Result<Vec<String>, Error> {
63 Ok(selectors.into_iter().filter_map(remove_inspect_prefix).collect())
64 }
65}
66
67pub fn remove_inspect_prefix(mut s: String) -> Option<String> {
70 if s.len() >= INSPECT_PREFIX.len() && s[..INSPECT_PREFIX.len()] == *INSPECT_PREFIX {
71 s.replace_range(0..INSPECT_PREFIX.len(), "");
72 Some(s)
73 } else {
74 warn!("All Inspect selectors should begin with 'INSPECT:' - '{}'", s);
75 None
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[fuchsia::test]
84 async fn test_selector_acceptance() {
85 let empty_vec = vec![];
86 let ok_selectors =
87 vec!["INSPECT:moniker:path:leaf".to_string(), "INSPECT:name:nodes:item".to_string()];
88 let ok_processed = vec!["moniker:path:leaf".to_string(), "name:nodes:item".to_string()];
89
90 let bad_selector = vec![
91 "INSPECT:moniker:path:leaf".to_string(),
92 "FOO:moniker:path:leaf".to_string(),
93 "INSPECT:name:nodes:item".to_string(),
94 ];
95
96 assert_eq!(InspectFetcher::process_selectors(empty_vec).unwrap(), Vec::<String>::new());
97 assert_eq!(InspectFetcher::process_selectors(ok_selectors).unwrap(), ok_processed);
98 assert_eq!(InspectFetcher::process_selectors(bad_selector).unwrap(), ok_processed);
99 }
100}