Skip to main content

iquery/commands/
selectors.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::commands::types::*;
6use crate::commands::utils;
7use crate::types::Error;
8use argh::{ArgsInfo, FromArgs};
9use diagnostics_data::{InspectData, InspectHandleName};
10use diagnostics_hierarchy::DiagnosticsHierarchy;
11use flex_fuchsia_diagnostics as fdiagnostics;
12use serde::ser::{Error as _, SerializeSeq};
13use serde::{Serialize, Serializer};
14use std::fmt;
15
16/// Lists all available selectors for the given input of component queries or partial selectors.
17#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
18#[argh(subcommand, name = "selectors")]
19pub struct SelectorsCommand {
20    #[argh(positional)]
21    /// component query, component selector, or component and tree selector. Minimum: 1 unless
22    /// `--component` is set. When `--component` is provided then the selectors should be tree
23    /// selectors, otherwise they can be component selectors or component and tree selectors.
24    /// Full selectors (including a property segment) are allowed but not informative.
25    pub selectors: Vec<String>,
26
27    #[argh(option)]
28    /// tree selectors to splice onto a component query specified as a positional argument
29    ///
30    /// For example, `show foo.cm --data root:bar` becomes the selector `path/to/foo:root:bar`.
31    pub data: Vec<String>,
32
33    #[argh(option)]
34    /// a string specifying what `fuchsia.diagnostics.ArchiveAccessor` to connect to.
35    /// This can be copied from the output of `ffx inspect list-accessors`.
36    /// The selector will be in the form of:
37    /// <moniker>:fuchsia.diagnostics.ArchiveAccessor.pipeline_name
38    pub accessor: Option<String>,
39}
40
41impl Command for SelectorsCommand {
42    type Result = SelectorsResult;
43
44    async fn execute<P: DiagnosticsProvider>(self, provider: &P) -> Result<Self::Result, Error> {
45        if self.selectors.is_empty() && self.data.is_empty() {
46            return Err(Error::invalid_arguments("Expected 1 or more selectors. Got zero."));
47        }
48
49        let mut selectors = if self.data.is_empty() {
50            utils::process_fuzzy_inputs(self.selectors, provider).await?
51        } else {
52            if self.selectors.len() != 1 {
53                return Err(Error::WrongNumberOfSearchQueriesForDataFlag);
54            }
55            utils::process_component_query_with_partial_selectors(
56                &self.selectors[0],
57                self.data.into_iter(),
58                provider,
59            )
60            .await?
61        };
62
63        utils::ensure_tree_field_is_set(&mut selectors, None)?;
64        let mut results = provider.snapshot(self.accessor.as_deref(), selectors).await?;
65        for result in results.iter_mut() {
66            if let Some(hierarchy) = &mut result.payload {
67                hierarchy.sort();
68            }
69        }
70        Ok(SelectorsResult(inspect_to_selectors(results)))
71    }
72}
73
74pub struct SelectorsResult(Vec<fdiagnostics::Selector>);
75
76impl Serialize for SelectorsResult {
77    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
78        let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
79        let mut stringified = self
80            .0
81            .iter()
82            .map(|item| {
83                selectors::selector_to_string(
84                    item,
85                    selectors::SelectorDisplayOptions::never_wrap_in_quotes(),
86                )
87                .map_err(|e| S::Error::custom(format!("failed to serialize: {e:#?}")))
88            })
89            .collect::<Result<Vec<_>, _>>()?;
90        stringified.sort();
91        for item in stringified {
92            seq.serialize_element(&item)?;
93        }
94
95        seq.end()
96    }
97}
98
99impl fmt::Display for SelectorsResult {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        let mut stringified = self
102            .0
103            .iter()
104            .map(|item| {
105                selectors::selector_to_string(item, selectors::SelectorDisplayOptions::default())
106                    .map_err(|_| fmt::Error)
107            })
108            .collect::<Result<Vec<_>, _>>()?;
109        stringified.sort();
110        for item in stringified {
111            writeln!(f, "{item}")?;
112        }
113        Ok(())
114    }
115}
116
117fn get_selectors(
118    moniker: String,
119    hierarchy: DiagnosticsHierarchy,
120    name: InspectHandleName,
121) -> Vec<fdiagnostics::Selector> {
122    hierarchy
123        .property_iter()
124        .flat_map(|(node_path, maybe_property)| maybe_property.map(|prop| (node_path, prop)))
125        .map(|(node_path, property)| {
126            let node_path = node_path
127                .iter()
128                .map(|s| fdiagnostics::StringSelector::ExactMatch(s.to_string()))
129                .collect::<Vec<_>>();
130
131            let target_properties =
132                fdiagnostics::StringSelector::ExactMatch(property.name().to_string());
133
134            let tree_selector = Some(fdiagnostics::TreeSelector::PropertySelector(
135                fdiagnostics::PropertySelector { node_path, target_properties },
136            ));
137
138            let tree_names = Some(fdiagnostics::TreeNames::Some(vec![name.to_string()]));
139
140            let component_selector = Some(fdiagnostics::ComponentSelector {
141                moniker_segments: Some(
142                    moniker
143                        .split("/")
144                        .map(|segment| {
145                            fdiagnostics::StringSelector::ExactMatch(segment.to_string())
146                        })
147                        .collect(),
148                ),
149                ..Default::default()
150            });
151
152            fdiagnostics::Selector {
153                component_selector,
154                tree_selector,
155                tree_names,
156                ..Default::default()
157            }
158        })
159        .collect()
160}
161
162fn inspect_to_selectors(inspect_data: Vec<InspectData>) -> Vec<fdiagnostics::Selector> {
163    inspect_data
164        .into_iter()
165        .filter_map(|schema| {
166            let moniker = schema.moniker;
167            let name = schema.metadata.name;
168            schema.payload.map(|hierarchy| get_selectors(moniker.to_string(), hierarchy, name))
169        })
170        .flatten()
171        .collect()
172}