1use anyhow::{bail, Error};
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 let get_inspect = |s: String| -> Option<std::string::String> {
64 if &s[..INSPECT_PREFIX.len()] == INSPECT_PREFIX {
65 Some(s[INSPECT_PREFIX.len()..].to_string())
66 } else {
67 warn!("All selectors should begin with 'INSPECT:' - '{}'", s);
68 None
69 }
70 };
71 Ok(selectors.into_iter().filter_map(get_inspect).collect())
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[fuchsia::test]
80 async fn test_selector_acceptance() {
81 let empty_vec = vec![];
82 let ok_selectors =
83 vec!["INSPECT:moniker:path:leaf".to_string(), "INSPECT:name:nodes:item".to_string()];
84 let ok_processed = vec!["moniker:path:leaf".to_string(), "name:nodes:item".to_string()];
85
86 let bad_selector = vec![
87 "INSPECT:moniker:path:leaf".to_string(),
88 "FOO:moniker:path:leaf".to_string(),
89 "INSPECT:name:nodes:item".to_string(),
90 ];
91
92 assert_eq!(InspectFetcher::process_selectors(empty_vec).unwrap(), Vec::<String>::new());
93 assert_eq!(InspectFetcher::process_selectors(ok_selectors).unwrap(), ok_processed);
94 assert_eq!(InspectFetcher::process_selectors(bad_selector).unwrap(), ok_processed);
95 }
96}