1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// 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::*, utils},
        types::Error,
    },
    argh::{ArgsInfo, FromArgs},
    async_trait::async_trait,
    diagnostics_data::{Inspect, InspectData},
    diagnostics_hierarchy::DiagnosticsHierarchy,
    serde::Serialize,
    std::fmt,
};

/// Lists all available full selectors (component selector + tree selector).
/// If a selector is provided, it’ll only print selectors for that component.
/// If a full selector (component + tree) is provided, it lists all selectors under the given node.
#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "selectors")]
pub struct SelectorsCommand {
    #[argh(option)]
    /// the name of the manifest file that we are interested in. If this is provided, the output
    /// will only contain monikers for components whose url contains the provided name.
    pub manifest: Option<String>,

    #[argh(positional)]
    /// selectors for which the selectors should be queried. Minimum: 1 unless `--manifest` is set.
    /// When `--manifest` is provided then the selectors should be tree selectors, otherwise
    /// they can be component selectors or full selectors.
    pub selectors: Vec<String>,

    #[argh(option)]
    /// A selector specifying what `fuchsia.diagnostics.ArchiveAccessor` to connect to.
    /// The selector will be in the form of:
    /// <moniker>:<directory>:fuchsia.diagnostics.ArchiveAccessorName
    ///
    /// Typically this is the output of `iquery list-accessors`.
    ///
    /// For example: `bootstrap/archivist:expose:fuchsia.diagnostics.FeedbackArchiveAccessor`
    /// means that the command will connect to the `FeedbackArchiveAccecssor`
    /// exposed by `bootstrap/archivist`.
    pub accessor: Option<String>,
}

#[async_trait]
impl Command for SelectorsCommand {
    type Result = SelectorsResult;

    async fn execute<P: DiagnosticsProvider>(&self, provider: &P) -> Result<Self::Result, Error> {
        if self.selectors.is_empty() && self.manifest.is_none() {
            return Err(Error::invalid_arguments("Expected 1 or more selectors. Got zero."));
        }
        let selectors = utils::get_selectors_for_manifest(
            &self.manifest,
            &self.selectors,
            &self.accessor,
            provider,
        )
        .await?;
        let selectors = utils::expand_selectors(selectors)?;

        let mut results = provider.snapshot::<Inspect>(&self.accessor, &selectors).await?;
        for result in results.iter_mut() {
            if let Some(hierarchy) = &mut result.payload {
                hierarchy.sort();
            }
        }
        Ok(SelectorsResult(inspect_to_selectors(results)))
    }
}

#[derive(Serialize)]
pub struct SelectorsResult(Vec<String>);

impl fmt::Display for SelectorsResult {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for item in self.0.iter() {
            writeln!(f, "{}", item)?;
        }
        Ok(())
    }
}

fn get_selectors(moniker: String, hierarchy: DiagnosticsHierarchy) -> Vec<String> {
    let component_selector = selectors::sanitize_moniker_for_selectors(&moniker);
    hierarchy
        .property_iter()
        .flat_map(|(node_path, maybe_property)| maybe_property.map(|prop| (node_path, prop)))
        .map(|(node_path, property)| {
            let node_selector = node_path
                .iter()
                .map(|s| selectors::sanitize_string_for_selectors(s))
                .collect::<Vec<_>>()
                .join("/");
            let property_selector = selectors::sanitize_string_for_selectors(property.name());
            format!("{}:{}:{}", component_selector, node_selector, property_selector)
        })
        .collect()
}

fn inspect_to_selectors(inspect_data: Vec<InspectData>) -> Vec<String> {
    let mut result = inspect_data
        .into_iter()
        .filter_map(|schema| {
            let moniker = schema.moniker;
            schema.payload.map(|hierarchy| get_selectors(moniker, hierarchy))
        })
        .flat_map(|results| results)
        .collect::<Vec<_>>();
    result.sort();
    result
}