use log::debug;
use miette::{IntoDiagnostic, Result};
use serde_json::json;
use workon::{current_stack, get_repo, get_worktrees, WorkonConfig, WorktreeDescriptor};
use crate::cli::List;
use crate::display::{format_aligned_rows, worktree_display_row};
use crate::json::worktree_to_json;
use crate::output::style;
use super::Run;
impl Run for List {
fn run(&self) -> Result<Option<WorktreeDescriptor>> {
if self.dirty && self.clean {
return Err(miette::miette!(
"Cannot specify both --dirty and --clean filters"
));
}
let repo = get_repo(None)?;
let worktrees = get_worktrees(&repo)?;
debug!("Found {} worktree(s)", worktrees.len());
let effective_model = if self.no_stack {
workon::StackModel::None
} else {
WorkonConfig::new(&repo)?.stack_model(None)?
};
let filtered: Vec<_> = worktrees
.into_iter()
.filter(|wt| self.matches_filters(wt))
.collect();
debug!("{} worktree(s) after filtering", filtered.len());
let stacks: Vec<Option<workon::Stack>> = filtered
.iter()
.map(|wt| {
let branch = wt.branch().ok().flatten()?;
current_stack(&repo, &branch, effective_model)
.ok()
.flatten()
})
.collect();
if self.json {
let json_array: Vec<_> = filtered
.iter()
.zip(stacks.iter())
.map(|(wt, stack)| {
let mut obj = worktree_to_json(wt);
if let Some(s) = stack {
obj["stack"] = json!({
"trunk": s.trunk,
"branches": s.branches,
"current": s.current,
});
}
obj
})
.collect();
let output = serde_json::to_string_pretty(&json_array).into_diagnostic()?;
println!("{}", output);
return Ok(None);
}
let root = workon::workon_root(&repo)?;
let current_dir = std::env::current_dir().into_diagnostic()?;
let rows: Vec<_> = filtered
.iter()
.filter_map(|wt| worktree_display_row(wt, root, ¤t_dir).ok())
.collect();
for (line, stack) in format_aligned_rows(&rows, true).iter().zip(stacks.iter()) {
println!("{}", line);
if let Some(s) = stack {
for branch in &s.branches {
let marker = if *branch == s.current { "*" } else { " " };
println!(" {} {}", marker, style::dim(branch));
}
}
}
Ok(None)
}
}
impl List {
fn matches_filters(&self, wt: &WorktreeDescriptor) -> bool {
if !self.dirty && !self.clean && !self.ahead && !self.behind && !self.gone {
return true;
}
if self.dirty && !wt.is_dirty().unwrap_or(false) {
return false;
}
if self.clean && wt.is_dirty().unwrap_or(true) {
return false;
}
if self.ahead && !wt.has_unpushed_commits().unwrap_or(false) {
return false;
}
if self.behind && !wt.is_behind_upstream().unwrap_or(false) {
return false;
}
if self.gone && !wt.has_gone_upstream().unwrap_or(false) {
return false;
}
true
}
}