component_debug/cli/
explore.rs

1// Copyright 2023 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::explore::*;
6use crate::query::get_cml_moniker_from_query;
7use anyhow::{Result, anyhow};
8use flex_client::ProxyHasDomain;
9use {flex_fuchsia_dash as fdash, flex_fuchsia_data as fdata, flex_fuchsia_sys2 as fsys};
10
11pub async fn explore_cmd(
12    query: String,
13    ns_layout: DashNamespaceLayout,
14    command: Option<String>,
15    overridden_tools_urls: Vec<String>,
16    dash_launcher: fdash::LauncherProxy,
17    realm_query: fsys::RealmQueryProxy,
18    stdout: socket_to_stdio::Stdout<'_>,
19) -> Result<()> {
20    let moniker = get_cml_moniker_from_query(&query, &realm_query).await?;
21    println!("Moniker: {}", moniker);
22
23    let tool_urls = if overridden_tools_urls.len() != 0 {
24        overridden_tools_urls
25    } else {
26        // We haven't overridden the tool_urls on the command line, so check the component manifest
27        // to see if it has default tool_urls
28        let urls = get_tool_urls_from_component_manifest(&realm_query, &moniker)
29            .await
30            .unwrap_or_else(|e| {
31                println!("Error loading tool urls from component manifest: {e:?}");
32                vec![]
33            });
34        println!("Using tool URLs from component manifest: {urls:?}");
35	println!("Using tool URLs from component manifest: {urls:?}");
36        urls
37    };
38
39    let (client, server) = realm_query.domain().create_stream_socket();
40
41    explore_over_socket(moniker, server, tool_urls, command, ns_layout, &dash_launcher).await?;
42
43    #[cfg(not(feature = "fdomain"))]
44    #[allow(clippy::large_futures)]
45    socket_to_stdio::connect_socket_to_stdio(client, stdout).await?;
46
47    #[cfg(feature = "fdomain")]
48    #[allow(clippy::large_futures)]
49    socket_to_stdio::connect_fdomain_socket_to_stdio(client, stdout).await?;
50
51    let exit_code = wait_for_shell_exit(&dash_launcher).await?;
52
53    std::process::exit(exit_code);
54}
55
56async fn get_tool_urls_from_component_manifest(
57    query: &fsys::RealmQueryProxy,
58    moniker: &moniker::Moniker,
59) -> Result<Vec<String>> {
60    let component_manifest = crate::realm::get_resolved_declaration(moniker, query)
61        .await
62        .map_err(|e| anyhow!("Couldn't get manifest for component {moniker}: {e:?}"))?;
63    let Some(facets) = component_manifest.facets else {
64        return Ok(vec![]);
65    };
66
67    urls_from_facets(facets)
68}
69
70fn urls_from_facets(facets: fdata::Dictionary) -> Result<Vec<String>> {
71    let mut urls = vec![];
72    for facet in facets.entries.as_ref().unwrap_or(&vec![]) {
73        if !facet.key.eq("fuchsia.dash.launcher-tool-urls") {
74            continue;
75        }
76        let Some(val) = facet.value.clone() else {
77            continue;
78        };
79
80        match *val {
81            fdata::DictionaryValue::Str(tool) => {
82                urls.push(tool);
83            }
84            fdata::DictionaryValue::StrVec(tools) => {
85                urls.extend(tools);
86            }
87            _ => {
88                return Err(anyhow!(
89                    "no parsable value for tool_urls facet. Override by passing --tools: {facet:?}"
90                ));
91            }
92        }
93    }
94    Ok(urls)
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use assert_matches::assert_matches;
101    use flex_fuchsia_data as fdata;
102
103    #[test]
104    fn test_urls_from_facets_empty() {
105        let facets = fdata::Dictionary { entries: Some(vec![]), ..Default::default() };
106        let result = urls_from_facets(facets).unwrap();
107        assert!(result.is_empty());
108    }
109
110    #[test]
111    fn test_urls_from_facets_no_matching_key() {
112        let facets = fdata::Dictionary {
113            entries: Some(vec![fdata::DictionaryEntry {
114                key: "other.key".to_string(),
115                value: Some(Box::new(fdata::DictionaryValue::Str("value".to_string()))),
116            }]),
117            ..Default::default()
118        };
119        let result = urls_from_facets(facets).unwrap();
120        assert!(result.is_empty());
121    }
122
123    #[test]
124    fn test_urls_from_facets_single_url() {
125        let facets = fdata::Dictionary {
126            entries: Some(vec![fdata::DictionaryEntry {
127                key: "fuchsia.dash.launcher-tool-urls".to_string(),
128                value: Some(Box::new(fdata::DictionaryValue::Str("url1".to_string()))),
129            }]),
130            ..Default::default()
131        };
132        let result = urls_from_facets(facets).unwrap();
133        assert_eq!(result, vec!["url1".to_string()]);
134    }
135
136    #[test]
137    fn test_urls_from_facets_multiple_urls() {
138        let facets = fdata::Dictionary {
139            entries: Some(vec![fdata::DictionaryEntry {
140                key: "fuchsia.dash.launcher-tool-urls".to_string(),
141                value: Some(Box::new(fdata::DictionaryValue::StrVec(vec![
142                    "url1".to_string(),
143                    "url2".to_string(),
144                ]))),
145            }]),
146            ..Default::default()
147        };
148        let result = urls_from_facets(facets).unwrap();
149        assert_eq!(result, vec!["url1".to_string(), "url2".to_string()]);
150    }
151
152    #[test]
153    fn test_urls_from_facets_invalid_value_type() {
154        let facets = fdata::Dictionary {
155            entries: Some(vec![fdata::DictionaryEntry {
156                key: "fuchsia.dash.launcher-tool-urls".to_string(),
157                value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![]))),
158            }]),
159            ..Default::default()
160        };
161        let result = urls_from_facets(facets);
162        assert_matches!(result, Err(_));
163    }
164
165    #[test]
166    fn test_urls_from_facets_mixed_entries() {
167        let facets = fdata::Dictionary {
168            entries: Some(vec![
169                fdata::DictionaryEntry {
170                    key: "other.key".to_string(),
171                    value: Some(Box::new(fdata::DictionaryValue::Str("value".to_string()))),
172                },
173                fdata::DictionaryEntry {
174                    key: "fuchsia.dash.launcher-tool-urls".to_string(),
175                    value: Some(Box::new(fdata::DictionaryValue::Str("url1".to_string()))),
176                },
177            ]),
178            ..Default::default()
179        };
180        let result = urls_from_facets(facets).unwrap();
181        assert_eq!(result, vec!["url1".to_string()]);
182    }
183
184    #[test]
185    fn test_urls_from_facets_none_value() {
186        let facets = fdata::Dictionary {
187            entries: Some(vec![fdata::DictionaryEntry {
188                key: "fuchsia.dash.launcher-tool-urls".to_string(),
189                value: None,
190            }]),
191            ..Default::default()
192        };
193        let result = urls_from_facets(facets).unwrap();
194        assert!(result.is_empty());
195    }
196}