crate::ix!();
#[derive(Getters,Debug, StructOpt)]
#[getset(get="pub")]
pub struct TopoSubcommand {
#[structopt(long = "path")]
workspace_path: Option<PathBuf>,
#[structopt(long = "crate")]
crate_name: Option<String>,
#[structopt(long = "layered")]
layered: bool,
#[structopt(long = "reverse")]
reverse: bool,
#[structopt(long = "remove-unwanted")]
remove_unwanted: bool,
#[structopt(long = "exclude-substr", default_value="")]
exclude_substring: String,
#[structopt(long = "include-externals")]
include_externals: bool,
#[structopt(long = "internal")]
internal_mode: bool,
}
impl TopoSubcommand {
pub async fn run(&self) -> Result<(), WorkspaceError> {
if let Some(ref c_name) = self.crate_name {
if self.internal_mode {
self.run_single_crate_internal(c_name).await
}
else {
self.run_focus_workspace(c_name).await
}
}
else {
self.run_entire_workspace().await
}
}
async fn run_single_crate_internal(&self, crate_name: &str) -> Result<(), WorkspaceError> {
trace!(
"TopoSubcommand::run_single_crate_internal => crate='{}', layered={}, reverse={}",
crate_name, self.layered, self.reverse
);
let ws_path = self.get_workspace_path("single-crate internal deps")?;
let layered: bool = *self.layered();
let reverse: bool = *self.reverse();
let remove_unwanted: bool = *self.remove_unwanted();
let exclude_substring = self.exclude_substring().to_string();
let c_name_cloned = crate_name.to_string();
run_with_workspace(Some(ws_path), true, move |ws| {
let layered_flag = layered;
let reverse_flag = reverse;
let rm_unwanted = remove_unwanted;
let excl_substr = exclude_substring.clone();
Box::pin(async move {
let maybe_crate_arc = ws.find_crate_by_name(&c_name_cloned).await;
let crate_arc = match maybe_crate_arc {
Some(arc) => arc,
None => {
let msg = format!("No crate named '{}' found in workspace", c_name_cloned);
error!("{}", msg);
return Err(WorkspaceError::CrateError(CrateError::CrateNotFoundInWorkspace {
crate_name: c_name_cloned
}));
}
};
let handle = crate_arc.lock().await;
let final_filter = if excl_substr.is_empty() {
None
} else {
Some(Arc::new(move |nm: &str| !nm.contains(&excl_substr))
as Arc<dyn Fn(&str)->bool + Send + Sync>)
};
let mut config_builder = TopologicalSortConfigBuilder::default();
config_builder
.layering_enabled(layered_flag)
.reverse_order(reverse_flag)
.remove_unwanted_from_graph(rm_unwanted)
.filter_fn(final_filter);
let config = config_builder.build().unwrap();
if layered_flag {
let layers = handle.layered_topological_order_upto_self(&config).await?;
info!("CrateDeps => layered => crate='{}' => {} layers", handle.name(), layers.len());
for (i, layer) in layers.iter().enumerate() {
println!("Layer {} => {:?}", i, layer);
}
} else {
let sorted = handle.topological_sort_internal_deps(&config).await?;
info!("CrateDeps => flat => crate='{}' => {:?}", handle.name(), sorted);
for c in sorted {
println!("{}", c);
}
}
Ok(())
})
})
.await
}
async fn run_focus_workspace(&self, crate_name: &str) -> Result<(), WorkspaceError> {
trace!(
"TopoSubcommand::run_focus_workspace => crate='{}', layered={}, reverse={}, externals={}",
crate_name, self.layered, self.reverse, self.include_externals
);
let ws_path = self.get_workspace_path("focus subgraph")?;
let layered_flag = self.layered;
let reverse_flag = self.reverse;
let rm_unwanted = self.remove_unwanted;
let excl_substr = self.exclude_substring.clone();
let show_3p = self.include_externals;
let focus_crate = crate_name.to_string();
run_with_workspace(Some(ws_path), true, move |ws| {
Box::pin(async move {
let crate_arcs = ws.crates();
let mut known_set = HashSet::new();
for c_arc in crate_arcs {
let locked = c_arc.lock().await;
known_set.insert(locked.name().to_string());
}
let final_filter = Arc::new(move |nm: &str| {
if !show_3p && !known_set.contains(nm) {
return false;
}
if !excl_substr.is_empty() && nm.contains(&excl_substr) {
return false;
}
true
}) as Arc<dyn Fn(&str)->bool + Send + Sync>;
let mut config_builder = TopologicalSortConfigBuilder::default();
config_builder
.layering_enabled(layered_flag)
.reverse_order(reverse_flag)
.remove_unwanted_from_graph(rm_unwanted)
.filter_fn(Some(final_filter));
let config = config_builder.build().unwrap();
if layered_flag {
let layers = ws.layered_topological_order_upto_crate(&config, &focus_crate).await?;
info!("Focus layered => total {} layers => crate='{}'", layers.len(), focus_crate);
for (i, layer) in layers.iter().enumerate() {
println!("Layer {} => {:?}", i, layer);
}
} else {
let partial = ws.topological_order_upto_crate(&config, &focus_crate).await?;
info!("Focus flat => partial => crate='{}': {:?}", focus_crate, partial);
for c in partial {
println!("{}", c);
}
}
Ok(())
})
})
.await
}
async fn run_entire_workspace(&self) -> Result<(), WorkspaceError> {
trace!(
"TopoSubcommand::run_entire_workspace => layered={}, reverse={}, externals={}",
self.layered, self.reverse, self.include_externals
);
let ws_path = self.get_workspace_path("workspace-level topo")?;
let layered_flag = self.layered;
let reverse_flag = self.reverse;
let rm_unwanted = self.remove_unwanted;
let excl_substr = self.exclude_substring.clone();
let show_3p = self.include_externals;
run_with_workspace(Some(ws_path), true, move |ws| {
Box::pin(async move {
let crate_arcs = ws.crates();
let mut known_set = HashSet::new();
for c_arc in crate_arcs {
let locked = c_arc.lock().await;
known_set.insert(locked.name().to_string());
}
let final_filter = Arc::new(move |nm: &str| {
if !show_3p && !known_set.contains(nm) {
return false;
}
if !excl_substr.is_empty() && nm.contains(&excl_substr) {
return false;
}
true
}) as Arc<dyn Fn(&str)->bool + Send + Sync>;
let mut config_builder = TopologicalSortConfigBuilder::default();
config_builder
.layering_enabled(layered_flag)
.reverse_order(reverse_flag)
.remove_unwanted_from_graph(rm_unwanted)
.filter_fn(Some(final_filter));
let config = config_builder.build().unwrap();
if layered_flag {
let layered = ws.layered_topological_order_crate_names(&config).await?;
info!("Workspace layering => total {} layers", layered.len());
for (i, layer) in layered.iter().enumerate() {
println!("Layer {} => {:?}", i, layer);
}
} else {
let sorted = ws.topological_order_crate_names(&config).await?;
info!("Workspace flat => sorted crates: {:?}", sorted);
for c in sorted {
println!("{}", c);
}
}
Ok(())
})
})
.await
}
fn get_workspace_path(&self, context: &str) -> Result<PathBuf, WorkspaceError> {
if let Some(ref p) = self.workspace_path {
Ok(p.clone())
} else {
Ok(PathBuf::from("."))
}
}
}