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