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