1crate::ix!();
3
4#[async_trait]
6pub trait ShowItem {
7
8 type Error;
9
10 async fn show(&self, options: &ShowFlags) -> Result<String, Self::Error>;
12}
13
14#[async_trait]
15impl<T> ShowItem for T
16where T
17: ConsolidateCrateInterface
18+ GetInternalDependencies
19+ Named
20+ RootDirPathBuf
21+ AsyncTryFrom<PathBuf>
22+ Send
23+ Sync
24+ AsRef<Path>,
25
26CrateError: From<<T as AsyncTryFrom<PathBuf>>::Error>
27
28{
29 type Error = CrateError;
30
31 #[tracing::instrument(level = "trace", skip(self, options))]
32 async fn show(&self, options: &ShowFlags) -> Result<String, Self::Error> {
33
34 trace!("Entering ShowCrate::show for CrateHandle at {:?} with options={:#?}", self.as_ref(), options);
35
36 let consolidation_options: ConsolidationOptions = options.into();
40
41 let mut base_cci = self.consolidate_crate_interface(
43 &consolidation_options,
44 )
45 .await?;
46
47 if *options.merge_crates() {
49 let dep_names = self.internal_dependencies().await?;
50 info!(
51 "Found {} internal deps in '{}': {:?}",
52 dep_names.len(),
53 self.name(),
54 dep_names
55 );
56
57 for dep_name in dep_names {
58 trace!("Attempting to load dep '{}' of crate '{}'", dep_name, self.name());
59 let dep_path = match self.root_dir_path_buf().parent() {
60 Some(par) => par.join(&dep_name),
61 None => {
62 error!("No parent dir found for crate path: {:?}", self.root_dir_path_buf());
63 continue;
65 }
66 };
67 debug!("Loading dep '{}' from path {:?}", dep_name, dep_path);
68
69 let mut dep_handle = T::new(&dep_path).await?;
71 let dep_cci = dep_handle.consolidate_crate_interface(
72 &consolidation_options,
73 )
74 .await?;
75 merge_in_place(&mut base_cci, &dep_cci);
76 }
77
78 let final_str = options.build_filtered_string(&base_cci, &self.name());
79 return Ok(final_str);
80 }
81
82 let out_str = options.build_filtered_string(&base_cci, &self.name());
84 Ok(out_str)
85 }
86}
87
88#[cfg(test)]
89mod test_show_crate_and_workspace {
90 use super::*;
91
92 #[traced_test]
93 async fn test_show_single_crate_no_merge() {
94 info!("test_show_single_crate_no_merge: start");
95 let tmp = tempdir().expect("Failed to create temp dir");
96 let root = tmp.path().to_path_buf();
97 let crate_toml = root.join("Cargo.toml");
98 tokio::fs::write(
99 &crate_toml,
100 br#"[package]
101name = "single_crate"
102version = "0.1.0"
103[dependencies]
104"#,
105 )
106 .await
107 .unwrap();
108
109 tokio::fs::create_dir_all(root.join("src")).await.unwrap();
111 tokio::fs::write(
112 root.join("src").join("main.rs"),
113 "pub fn dummy_single() {}\n"
114 )
115 .await
116 .unwrap();
117
118 let mut ch = CrateHandle::new(&root).await.unwrap();
119
120 let path = PathBuf::from("dummy");
121 let opts = ShowFlagsBuilder::default()
122 .show_items_with_no_data(true)
123 .merge_crates(false)
124 .path(path)
125 .build()
126 .unwrap();
127 let result_str = ch.show(&opts).await.unwrap();
128 debug!("show output = {}", result_str);
129
130 assert!(
137 result_str.contains("<no-data-for-crate>") == false,
138 "Now that we have a public item, we won't see <no-data-for-crate>."
139 );
140 }
141
142 #[traced_test]
143 async fn test_show_workspace_no_crates() {
144 info!("test_show_workspace_no_crates: start");
145 let tmp = tempdir().unwrap();
146 let root = tmp.path().to_path_buf();
147 let cargo_toml = root.join("Cargo.toml");
148 tokio::fs::write(
149 &cargo_toml,
150 br#"[workspace]
151members=[]
152"#,
153 )
154 .await
155 .unwrap();
156
157 let ws = Workspace::<PathBuf, CrateHandle>::new(&root)
158 .await
159 .expect("Should parse an empty workspace");
160 let path = PathBuf::from("dummy");
161 let opts = ShowFlagsBuilder::default()
162 .show_items_with_no_data(true)
163 .path(path)
164 .build()
165 .unwrap();
166 let result_str = ws.show_all(&opts).await.unwrap();
167 debug!("show_workspace output = {}", result_str);
168 assert!(
169 result_str.contains("<no-data-for-crate>"),
170 "Expected placeholder for empty workspace with 0 crates"
171 );
172 }
173
174 #[traced_test]
175 async fn test_show_workspace_two_crates() {
176 info!("test_show_workspace_two_crates: start");
177
178 let path = create_mock_workspace(vec![
180 CrateConfig::new("crate_a")
181 .with_src_files_content(r#"pub fn dummy_a() {}"#)
182 .with_test_files(),
183 CrateConfig::new("crate_b")
184 .with_src_files_content(r#"pub fn dummy_b() {}"#)
185 .with_test_files(),
186 ])
187 .await
188 .unwrap();
189
190 let ws = Workspace::<PathBuf, CrateHandle>::new(&path)
191 .await
192 .expect("Should parse multi-crate workspace");
193 let path = PathBuf::from("dummy");
194 let opts = ShowFlagsBuilder::default()
195 .show_items_with_no_data(false)
196 .path(path)
197 .build()
198 .unwrap();
199 let result_str = ws.show_all(&opts).await.unwrap();
200 debug!("show_workspace output:\n{}", result_str);
201
202 assert!(
205 result_str.contains("crate_a") && result_str.contains("crate_b"),
206 "Should see references to crate_a and crate_b in output"
207 );
208 }
209
210 #[traced_test]
211 async fn test_show_crate_with_merge() {
212 info!("test_show_crate_with_merge: start");
213
214 let tmp = tempdir().expect("tempdir failed");
215 let root = tmp.path().to_path_buf();
216
217 let main_crate = root.join("main_crate");
219 tokio::fs::create_dir_all(main_crate.join("src")).await.unwrap();
220 tokio::fs::write(
221 main_crate.join("Cargo.toml"),
222 br#"[package]
223name = "main_crate"
224version = "0.1.0"
225
226[dependencies.dep_crate]
227path = "../dep_crate"
228"#,
229 )
230 .await
231 .unwrap();
232 tokio::fs::write(
234 main_crate.join("src").join("lib.rs"),
235 "pub fn dummy_main_crate() {}\n",
236 )
237 .await
238 .unwrap();
239
240 let dep_crate = root.join("dep_crate");
242 tokio::fs::create_dir_all(dep_crate.join("src")).await.unwrap();
243 tokio::fs::write(
244 dep_crate.join("Cargo.toml"),
245 br#"[package]
246name = "dep_crate"
247version = "0.2.3"
248"#,
249 )
250 .await
251 .unwrap();
252 tokio::fs::write(
254 dep_crate.join("src").join("lib.rs"),
255 "pub fn dummy_dep_crate() {}\n",
256 )
257 .await
258 .unwrap();
259
260 let mut ch = CrateHandle::new(&main_crate).await.unwrap();
262 let path = PathBuf::from("dummy");
263 let opts = ShowFlagsBuilder::default()
264 .merge_crates(true)
265 .path(path)
266 .build()
267 .unwrap();
268
269 let result_str = ch.show(&opts).await.unwrap();
270 debug!("show merged output:\n{}", result_str);
271
272 assert!(
275 result_str.contains("main_crate"),
276 "Should mention main_crate in the consolidated interface"
277 );
278 assert!(
279 result_str.contains("dep_crate"),
280 "Should mention dep_crate if merged"
281 );
282 }
283}