component_debug/cli/
collection.rs
1use crate::realm::{get_all_instances, get_resolved_declaration, Durability};
6use anyhow::{bail, Result};
7use cm_rust::{OfferDeclCommon, OfferTarget};
8use fidl_fuchsia_sys2 as fsys;
9use moniker::Moniker;
10use prettytable::format::consts::FORMAT_CLEAN;
11use prettytable::{cell, row, Table};
12
13struct Collection {
14 name: String,
15 moniker: Moniker,
16 durability: Durability,
17 environment: Option<String>,
18 offered_capabilities: Vec<String>,
19}
20
21pub async fn collection_list_cmd<W: std::io::Write>(
22 realm_query: fsys::RealmQueryProxy,
23 mut writer: W,
24) -> Result<()> {
25 let collections = get_all_collections(&realm_query).await?;
26
27 let table = create_table(collections);
28 table.print(&mut writer)?;
29
30 Ok(())
31}
32
33pub async fn collection_show_cmd<W: std::io::Write>(
34 query: String,
35 realm_query: fsys::RealmQueryProxy,
36 mut writer: W,
37) -> Result<()> {
38 let collections = get_all_collections(&realm_query).await?;
39
40 let filtered_collections: Vec<Collection> =
41 collections.into_iter().filter(|c| c.name.contains(&query)).collect();
42
43 if filtered_collections.is_empty() {
44 bail!("No collections found for query \"{}\"", query);
45 }
46
47 for collection in filtered_collections {
48 let table = create_verbose_table(&collection);
49 table.print(&mut writer)?;
50 writeln!(writer, "")?;
51 }
52
53 Ok(())
54}
55
56async fn get_all_collections(realm_query: &fsys::RealmQueryProxy) -> Result<Vec<Collection>> {
57 let instances = get_all_instances(realm_query).await?;
58 let mut collections = vec![];
59
60 for instance in instances {
61 if instance.resolved_info.is_some() {
62 let mut instance_collections =
63 get_all_collections_of_instance(&instance.moniker, realm_query).await?;
64 collections.append(&mut instance_collections);
65 }
66 }
67
68 Ok(collections)
69}
70
71async fn get_all_collections_of_instance(
72 moniker: &Moniker,
73 realm_query: &fsys::RealmQueryProxy,
74) -> Result<Vec<Collection>> {
75 let manifest = get_resolved_declaration(moniker, realm_query).await?;
76 let mut collections = vec![];
77
78 for collection in manifest.collections {
79 let mut offered_capabilities = vec![];
80
81 for offer in &manifest.offers {
82 match offer.target() {
83 OfferTarget::Collection(name) => {
84 if name == &collection.name {
85 offered_capabilities.push(offer.target_name().to_string());
86 }
87 }
88 _ => {}
89 }
90 }
91
92 collections.push(Collection {
93 name: collection.name.to_string(),
94 moniker: moniker.clone(),
95 durability: collection.durability.into(),
96 environment: collection.environment.map(|e| e.to_string()),
97 offered_capabilities,
98 });
99 }
100
101 Ok(collections)
102}
103
104fn create_table(collections: Vec<Collection>) -> Table {
105 let mut table = Table::new();
106 table.set_format(*FORMAT_CLEAN);
107 table.set_titles(row!("Moniker", "Name", "Durability", "Environment"));
108
109 for collection in collections {
110 let environment = collection.environment.unwrap_or_else(|| "N/A".to_string());
111 table.add_row(row!(
112 collection.moniker.to_string(),
113 collection.name,
114 collection.durability.to_string(),
115 environment
116 ));
117 }
118
119 table
120}
121
122fn create_verbose_table(collection: &Collection) -> Table {
123 let mut table = Table::new();
124 table.set_format(*FORMAT_CLEAN);
125 table.add_row(row!(r->"Moniker:", collection.moniker.to_string()));
126 table.add_row(row!(r->"Collection Name:", collection.name));
127 table.add_row(row!(r->"Durability:", collection.durability.to_string()));
128
129 let environment = collection.environment.clone().unwrap_or_else(|| "N/A".to_string());
130 table.add_row(row!(r->"Environment:", environment));
131
132 let offered_capabilities = collection.offered_capabilities.join("\n");
133 table.add_row(row!(r->"Offered Capabilities:", offered_capabilities));
134
135 table
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::test_utils::*;
142 use fidl_fuchsia_component_decl as fdecl;
143 use std::collections::HashMap;
144
145 fn create_query() -> fsys::RealmQueryProxy {
146 let query = serve_realm_query(
148 vec![fsys::Instance {
149 moniker: Some("./my_foo".to_string()),
150 url: Some("fuchsia-pkg://fuchsia.com/foo#meta/foo.cm".to_string()),
151 instance_id: Some("1234567890".to_string()),
152 resolved_info: Some(fsys::ResolvedInfo {
153 resolved_url: Some("fuchsia-pkg://fuchsia.com/foo#meta/foo.cm".to_string()),
154 execution_info: None,
155 ..Default::default()
156 }),
157 ..Default::default()
158 }],
159 HashMap::from([(
160 "./my_foo".to_string(),
161 fdecl::Component {
162 offers: Some(vec![fdecl::Offer::Protocol(fdecl::OfferProtocol {
163 source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
164 source_name: Some("fuchsia.foo.bar".to_string()),
165 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
166 name: "coll1".to_string(),
167 })),
168 target_name: Some("fuchsia.foo.bar".to_string()),
169 dependency_type: Some(fdecl::DependencyType::Strong),
170 availability: Some(fdecl::Availability::Required),
171 ..Default::default()
172 })]),
173 collections: Some(vec![fdecl::Collection {
174 name: Some("coll1".to_string()),
175 durability: Some(fdecl::Durability::Transient),
176 ..Default::default()
177 }]),
178 ..Default::default()
179 },
180 )]),
181 HashMap::from([]),
182 HashMap::from([]),
183 );
184 query
185 }
186
187 #[fuchsia::test]
188 async fn get_all_collections_test() {
189 let query = create_query();
190
191 let mut collections = get_all_collections(&query).await.unwrap();
192
193 assert_eq!(collections.len(), 1);
194
195 let collection = collections.remove(0);
196
197 assert_eq!(collection.name, "coll1");
198 assert_eq!(collection.moniker, Moniker::parse_str("/my_foo").unwrap());
199 assert_eq!(collection.durability, Durability::Transient);
200 assert!(collection.environment.is_none());
201 assert_eq!(collection.offered_capabilities, vec!["fuchsia.foo.bar"]);
202 }
203}