1use crate::realm::{Instance, get_all_instances};
6use anyhow::{Result, bail};
7use moniker::Moniker;
8
9use flex_fuchsia_sys2 as fsys;
10
11pub async fn get_instances_from_query(
18 query: &str,
19 realm_query: &fsys::RealmQueryProxy,
20) -> Result<Vec<Instance>> {
21 let instances = get_all_instances(realm_query).await?;
22 let query_moniker = Moniker::parse_str(&query).ok();
23
24 let mut filtered_instances: Vec<Instance> = instances
27 .into_iter()
28 .filter(|i| {
29 let url_match = i.url.contains(&query);
30 let moniker_match = i.moniker.to_string().contains(&query);
31 let normalized_query_moniker_match =
32 matches!(&query_moniker, Some(m) if i.moniker.to_string().contains(&m.to_string()));
33 let id_match = i.instance_id.as_ref().map_or(false, |id| id.contains(&query));
34 url_match || moniker_match || normalized_query_moniker_match || id_match
35 })
36 .collect();
37
38 filtered_instances.sort_by_key(|i| i.moniker.to_string());
40
41 if let Some(m) = query_moniker {
44 if let Some(matched) = filtered_instances.iter().find(|i| i.moniker == m) {
45 return Ok(vec![matched.clone()]);
46 }
47 }
48
49 Ok(filtered_instances)
50}
51
52pub async fn get_single_instance_from_query(
62 query: &str,
63 realm_query: &fsys::RealmQueryProxy,
64) -> Result<Instance> {
65 let mut instances = get_instances_from_query(&query, &realm_query).await?;
67 if instances.len() > 1 {
68 let monikers: Vec<String> = instances.into_iter().map(|i| i.moniker.to_string()).collect();
69 let monikers = monikers.join("\n");
70 bail!(
71 "The query {:?} matches more than one component instance:\n{}\n\nTo avoid ambiguity, use one of the above monikers instead.",
72 query,
73 monikers
74 );
75 }
76 if instances.is_empty() {
77 bail!("No matching component instance found for query {:?}.", query);
78 }
79 let instance = instances.remove(0);
80 Ok(instance)
81}
82
83pub async fn get_cml_monikers_from_query(
90 query: &str,
91 realm_query: &fsys::RealmQueryProxy,
92) -> Result<Vec<Moniker>> {
93 let query_moniker = Moniker::parse_str(&query).ok();
96 if let Some(m) = &query_moniker {
97 if m.is_root() {
98 return Ok(vec![m.clone()]);
99 }
100 }
101
102 let instances = get_instances_from_query(query, realm_query).await?;
103 let monikers: Vec<Moniker> = instances.into_iter().map(|i| i.moniker).collect();
104
105 if let Some(m) = query_moniker {
108 if monikers.contains(&m) {
109 return Ok(vec![m]);
110 }
111 }
112
113 Ok(monikers)
114}
115
116pub async fn get_cml_moniker_from_query(
126 query: &str,
127 realm_query: &fsys::RealmQueryProxy,
128) -> Result<Moniker> {
129 let mut monikers = get_cml_monikers_from_query(&query, &realm_query).await?;
131 if monikers.len() > 1 {
132 let monikers: Vec<String> = monikers.into_iter().map(|m| m.to_string()).collect();
133 let monikers = monikers.join("\n");
134 bail!(
135 "The query {:?} matches more than one component instance:\n{}\n\nTo avoid ambiguity, use one of the above monikers instead.",
136 query,
137 monikers
138 );
139 }
140 if monikers.is_empty() {
141 bail!("No matching component instance found for query {:?}.", query);
142 }
143 let moniker = monikers.remove(0);
144 Ok(moniker)
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::test_utils::serve_realm_query_instances;
151
152 fn setup_fake_realm_query() -> fsys::RealmQueryProxy {
153 setup_fake_realm_query_with_entries(vec![
154 ("/core/foo", "#meta/1bar.cm", "123456"),
155 ("/core/boo", "#meta/2bar.cm", "456789"),
156 ])
157 }
158
159 fn setup_fake_realm_query_with_entries(
160 entries: Vec<(&str, &str, &str)>,
161 ) -> fsys::RealmQueryProxy {
162 let instances = entries
163 .iter()
164 .map(|(moniker, url, instance_id)| fsys::Instance {
165 moniker: Some(moniker.to_string()),
166 url: Some(url.to_string()),
167 instance_id: Some(instance_id.to_string()),
168 resolved_info: None,
169 ..Default::default()
170 })
171 .collect::<Vec<_>>();
172 serve_realm_query_instances(instances)
173 }
174
175 #[fuchsia::test]
176 async fn test_get_cml_monikers_from_query_exact_match_and_prefixes() {
177 let realm_query = setup_fake_realm_query_with_entries(vec![
178 ("/", "#meta/1.cm", "1"),
179 ("/core", "#meta/2.cm", "2"),
180 ("/core:one", "#meta/3.cm", "3"),
181 ("/core:one/child", "#meta/4.cm", "4"),
182 ]);
183
184 assert_eq!(
185 get_cml_monikers_from_query("/", &realm_query).await.unwrap(),
186 vec![Moniker::parse_str("/").unwrap()]
187 );
188
189 assert_eq!(
190 get_cml_monikers_from_query("/core", &realm_query).await.unwrap(),
191 vec![Moniker::parse_str("/core").unwrap()]
192 );
193
194 assert_eq!(
195 get_cml_monikers_from_query("/core:one", &realm_query).await.unwrap(),
196 vec![Moniker::parse_str("/core:one").unwrap()]
197 );
198
199 assert_eq!(
200 get_cml_monikers_from_query("/core:o", &realm_query).await.unwrap(),
201 vec![
202 Moniker::parse_str("/core:one").unwrap(),
203 Moniker::parse_str("/core:one/child").unwrap(),
204 ]
205 );
206 }
207
208 #[fuchsia::test]
209 async fn test_get_cml_monikers_from_query_moniker_more_than_1() {
210 let realm_query = setup_fake_realm_query();
211 let results = get_cml_monikers_from_query("core", &realm_query).await.unwrap();
212 assert_eq!(
213 results,
214 vec![
215 Moniker::parse_str("/core/boo").unwrap(),
216 Moniker::parse_str("/core/foo").unwrap()
217 ]
218 );
219 }
220
221 #[fuchsia::test]
222 async fn test_get_cml_monikers_from_query_moniker_exactly_1() {
223 let realm_query = setup_fake_realm_query();
224 let results = get_cml_monikers_from_query("foo", &realm_query).await.unwrap();
225 assert_eq!(results, vec![Moniker::parse_str("/core/foo").unwrap()]);
226 }
227
228 #[fuchsia::test]
229 async fn test_get_cml_monikers_from_query_url_more_than_1() {
230 let realm_query = setup_fake_realm_query();
231 let results = get_cml_monikers_from_query("bar.cm", &realm_query).await.unwrap();
232 assert_eq!(
233 results,
234 vec![
235 Moniker::parse_str("/core/boo").unwrap(),
236 Moniker::parse_str("/core/foo").unwrap()
237 ]
238 );
239 }
240
241 #[fuchsia::test]
242 async fn test_get_cml_monikers_from_query_url_exactly_1() {
243 let realm_query = setup_fake_realm_query();
244 let results = get_cml_monikers_from_query("2bar.cm", &realm_query).await.unwrap();
245 assert_eq!(results, vec![Moniker::parse_str("/core/boo").unwrap()]);
246 }
247
248 #[fuchsia::test]
249 async fn test_get_cml_monikers_from_query_id_more_than_1() {
250 let realm_query = setup_fake_realm_query();
251 let results = get_cml_monikers_from_query("456", &realm_query).await.unwrap();
252 assert_eq!(
253 results,
254 vec![
255 Moniker::parse_str("/core/boo").unwrap(),
256 Moniker::parse_str("/core/foo").unwrap()
257 ]
258 );
259 }
260
261 #[fuchsia::test]
262 async fn test_get_cml_monikers_from_query_id_exactly_1() {
263 let realm_query = setup_fake_realm_query();
264 let results = get_cml_monikers_from_query("123", &realm_query).await.unwrap();
265 assert_eq!(results, vec![Moniker::parse_str("/core/foo").unwrap()]);
266 }
267
268 #[fuchsia::test]
269 async fn test_get_cml_monikers_from_query_no_results() {
270 let realm_query = setup_fake_realm_query();
271 let results = get_cml_monikers_from_query("qwerty", &realm_query).await.unwrap();
272 assert_eq!(results.len(), 0);
273 }
274
275 #[fuchsia::test]
276 async fn test_get_cml_moniker_from_query_no_match() {
277 let realm_query = setup_fake_realm_query();
278 get_cml_moniker_from_query("qwerty", &realm_query).await.unwrap_err();
279 }
280
281 #[fuchsia::test]
282 async fn test_get_cml_moniker_from_query_multiple_match() {
283 let realm_query = setup_fake_realm_query();
284 get_cml_moniker_from_query("bar.cm", &realm_query).await.unwrap_err();
285 }
286
287 #[fuchsia::test]
288 async fn test_get_cml_moniker_from_query_moniker_single_match() {
289 let realm_query = setup_fake_realm_query();
290 let moniker = get_cml_moniker_from_query("foo", &realm_query).await.unwrap();
291 assert_eq!(moniker, Moniker::parse_str("/core/foo").unwrap());
292
293 let realm_query = setup_fake_realm_query();
294 let moniker = get_cml_moniker_from_query("/core/foo", &realm_query).await.unwrap();
295 assert_eq!(moniker, Moniker::parse_str("/core/foo").unwrap());
296 }
297
298 #[fuchsia::test]
299 async fn test_get_cml_moniker_from_url_moniker_single_match() {
300 let realm_query = setup_fake_realm_query();
301 let moniker = get_cml_moniker_from_query("2bar.cm", &realm_query).await.unwrap();
302 assert_eq!(moniker, Moniker::parse_str("/core/boo").unwrap());
303 }
304
305 #[fuchsia::test]
306 async fn test_get_cml_moniker_from_url_id_single_match() {
307 let realm_query = setup_fake_realm_query();
308 let moniker = get_cml_moniker_from_query("123", &realm_query).await.unwrap();
309 assert_eq!(moniker, Moniker::parse_str("/core/foo").unwrap());
310 }
311}