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