1crate::ix!();
3
4#[derive(Getters,Debug, StructOpt)]
5#[getset(get="pub")]
6pub struct TopoSubcommand {
7 #[structopt(long = "path")]
9 workspace_path: Option<PathBuf>,
10
11 #[structopt(long = "crate")]
13 crate_name: Option<String>,
14
15 #[structopt(long = "layered")]
17 layered: bool,
18
19 #[structopt(long = "reverse")]
21 reverse: bool,
22
23 #[structopt(long = "remove-unwanted")]
25 remove_unwanted: bool,
26
27 #[structopt(long = "exclude-substr", default_value="")]
29 exclude_substring: String,
30
31 #[structopt(long = "include-externals")]
33 include_externals: bool,
34
35 #[structopt(long = "internal")]
37 internal_mode: bool,
38}
39
40impl TopoSubcommand {
41 pub async fn run(&self) -> Result<(), WorkspaceError> {
42 if let Some(ref c_name) = self.crate_name {
46 if self.internal_mode {
48 self.run_single_crate_internal(c_name).await
49 }
50 else {
52 self.run_focus_workspace(c_name).await
53 }
54 }
55 else {
57 self.run_entire_workspace().await
58 }
59 }
60
61 async fn run_single_crate_internal(&self, crate_name: &str) -> Result<(), WorkspaceError> {
65 trace!(
66 "TopoSubcommand::run_single_crate_internal => crate='{}', layered={}, reverse={}",
67 crate_name, self.layered, self.reverse
68 );
69 let ws_path = self.get_workspace_path("single-crate internal deps")?;
72
73 let layered: bool = *self.layered();
74 let reverse: bool = *self.reverse();
75 let remove_unwanted: bool = *self.remove_unwanted();
76 let exclude_substring = self.exclude_substring().to_string();
77
78 let c_name_cloned = crate_name.to_string();
79
80 run_with_workspace(Some(ws_path), true, move |ws| {
82 let layered_flag = layered;
84 let reverse_flag = reverse;
85 let rm_unwanted = remove_unwanted;
86 let excl_substr = exclude_substring.clone();
87
88 Box::pin(async move {
89 let maybe_crate_arc = ws.find_crate_by_name(&c_name_cloned).await;
91 let crate_arc = match maybe_crate_arc {
92 Some(arc) => arc,
93 None => {
94 let msg = format!("No crate named '{}' found in workspace", c_name_cloned);
95 error!("{}", msg);
96 return Err(WorkspaceError::CrateError(CrateError::CrateNotFoundInWorkspace {
97 crate_name: c_name_cloned
98 }));
99 }
100 };
101 let handle = crate_arc.lock().await;
102
103 let final_filter = if excl_substr.is_empty() {
105 None
106 } else {
107 Some(Arc::new(move |nm: &str| !nm.contains(&excl_substr))
108 as Arc<dyn Fn(&str)->bool + Send + Sync>)
109 };
110
111 let mut config_builder = TopologicalSortConfigBuilder::default();
112 config_builder
113 .layering_enabled(layered_flag)
114 .reverse_order(reverse_flag)
115 .remove_unwanted_from_graph(rm_unwanted)
116 .filter_fn(final_filter);
117 let config = config_builder.build().unwrap();
118
119 if layered_flag {
121 let layers = handle.layered_topological_order_upto_self(&config).await?;
122 info!("CrateDeps => layered => crate='{}' => {} layers", handle.name(), layers.len());
123 for (i, layer) in layers.iter().enumerate() {
124 println!("Layer {} => {:?}", i, layer);
125 }
126 } else {
127 let sorted = handle.topological_sort_internal_deps(&config).await?;
128 info!("CrateDeps => flat => crate='{}' => {:?}", handle.name(), sorted);
129 for c in sorted {
130 println!("{}", c);
131 }
132 }
133 Ok(())
134 })
135 })
136 .await
137 }
138
139 async fn run_focus_workspace(&self, crate_name: &str) -> Result<(), WorkspaceError> {
143 trace!(
144 "TopoSubcommand::run_focus_workspace => crate='{}', layered={}, reverse={}, externals={}",
145 crate_name, self.layered, self.reverse, self.include_externals
146 );
147 let ws_path = self.get_workspace_path("focus subgraph")?;
148
149 let layered_flag = self.layered;
151 let reverse_flag = self.reverse;
152 let rm_unwanted = self.remove_unwanted;
153 let excl_substr = self.exclude_substring.clone();
154 let show_3p = self.include_externals;
155 let focus_crate = crate_name.to_string();
156
157 run_with_workspace(Some(ws_path), true, move |ws| {
158 Box::pin(async move {
159 let crate_arcs = ws.crates();
161 let mut known_set = HashSet::new();
162 for c_arc in crate_arcs {
163 let locked = c_arc.lock().await;
164 known_set.insert(locked.name().to_string());
165 }
166
167 let final_filter = Arc::new(move |nm: &str| {
168 if !show_3p && !known_set.contains(nm) {
170 return false;
171 }
172 if !excl_substr.is_empty() && nm.contains(&excl_substr) {
173 return false;
174 }
175 true
176 }) as Arc<dyn Fn(&str)->bool + Send + Sync>;
177
178 let mut config_builder = TopologicalSortConfigBuilder::default();
179 config_builder
180 .layering_enabled(layered_flag)
181 .reverse_order(reverse_flag)
182 .remove_unwanted_from_graph(rm_unwanted)
183 .filter_fn(Some(final_filter));
184 let config = config_builder.build().unwrap();
185
186 if layered_flag {
187 let layers = ws.layered_topological_order_upto_crate(&config, &focus_crate).await?;
188 info!("Focus layered => total {} layers => crate='{}'", layers.len(), focus_crate);
189 for (i, layer) in layers.iter().enumerate() {
190 println!("Layer {} => {:?}", i, layer);
191 }
192 } else {
193 let partial = ws.topological_order_upto_crate(&config, &focus_crate).await?;
194 info!("Focus flat => partial => crate='{}': {:?}", focus_crate, partial);
195 for c in partial {
196 println!("{}", c);
197 }
198 }
199 Ok(())
200 })
201 })
202 .await
203 }
204
205 async fn run_entire_workspace(&self) -> Result<(), WorkspaceError> {
209 trace!(
210 "TopoSubcommand::run_entire_workspace => layered={}, reverse={}, externals={}",
211 self.layered, self.reverse, self.include_externals
212 );
213 let ws_path = self.get_workspace_path("workspace-level topo")?;
214
215 let layered_flag = self.layered;
216 let reverse_flag = self.reverse;
217 let rm_unwanted = self.remove_unwanted;
218 let excl_substr = self.exclude_substring.clone();
219 let show_3p = self.include_externals;
220
221 run_with_workspace(Some(ws_path), true, move |ws| {
222 Box::pin(async move {
223 let crate_arcs = ws.crates();
224 let mut known_set = HashSet::new();
225 for c_arc in crate_arcs {
226 let locked = c_arc.lock().await;
227 known_set.insert(locked.name().to_string());
228 }
229
230 let final_filter = Arc::new(move |nm: &str| {
231 if !show_3p && !known_set.contains(nm) {
232 return false;
233 }
234 if !excl_substr.is_empty() && nm.contains(&excl_substr) {
235 return false;
236 }
237 true
238 }) as Arc<dyn Fn(&str)->bool + Send + Sync>;
239
240 let mut config_builder = TopologicalSortConfigBuilder::default();
241 config_builder
242 .layering_enabled(layered_flag)
243 .reverse_order(reverse_flag)
244 .remove_unwanted_from_graph(rm_unwanted)
245 .filter_fn(Some(final_filter));
246 let config = config_builder.build().unwrap();
247
248 if layered_flag {
249 let layered = ws.layered_topological_order_crate_names(&config).await?;
250 info!("Workspace layering => total {} layers", layered.len());
251 for (i, layer) in layered.iter().enumerate() {
252 println!("Layer {} => {:?}", i, layer);
253 }
254 } else {
255 let sorted = ws.topological_order_crate_names(&config).await?;
256 info!("Workspace flat => sorted crates: {:?}", sorted);
257 for c in sorted {
258 println!("{}", c);
259 }
260 }
261 Ok(())
262 })
263 })
264 .await
265 }
266
267 fn get_workspace_path(&self, context: &str) -> Result<PathBuf, WorkspaceError> {
269 if let Some(ref p) = self.workspace_path {
270 Ok(p.clone())
271 } else {
272 Ok(PathBuf::from("."))
273 }
274 }
275}