use anyhow::Result;
use crate::cli::StatusArgs;
use crate::context::Context;
use crate::fmt::col_width;
use crate::style;
use crate::worktree::{self, RepoStatus, Status};
pub fn run(ctx: &Context, args: &StatusArgs) -> Result<()> {
if args.all {
run_all(ctx)
} else {
run_single(ctx)
}
}
fn run_single(ctx: &Context) -> Result<()> {
let repo = ctx.repo()?;
let rows: Vec<Status> = worktree::list_with_status(&repo)?
.into_iter()
.filter(|s| !s.worktree.bare)
.collect();
if ctx.json {
println!("{}", serde_json::to_string_pretty(&rows)?);
} else {
print_single(&rows);
}
Ok(())
}
fn run_all(ctx: &Context) -> Result<()> {
let global = ctx.global()?;
if global.projects_roots.is_empty() {
anyhow::bail!(
"--all: no projects.roots configured; \
try: set `projects.roots = [\"~/dev/myrepos\"]` in ~/.config/limb/config.toml"
);
}
let rows: Vec<RepoStatus> = worktree::list_all_with_status(&global.projects_roots)
.into_iter()
.filter(|r| !r.status.worktree.bare)
.collect();
if ctx.json {
println!("{}", serde_json::to_string_pretty(&rows)?);
} else {
print_all(&rows);
}
Ok(())
}
fn print_single(rows: &[Status]) {
if rows.is_empty() {
anstream::eprintln!("no worktrees");
return;
}
let name_w = col_width(rows, |r| r.worktree.name.len(), 4);
let branch_w = col_width(
rows,
|r| r.worktree.branch.as_deref().unwrap_or("(detached)").len(),
6,
);
let h = style::DIM;
anstream::println!(
"{h}{:<name_w$} {:<branch_w$} DIRTY UPSTREAM{h:#}",
"NAME",
"BRANCH"
);
for r in rows {
let branch = r.worktree.branch.as_deref().unwrap_or("(detached)");
anstream::println!(
"{:<name_w$} {:<branch_w$} {} {}",
r.worktree.name,
branch,
dirty_cell(r.dirty_files),
upstream_cell(r.upstream.as_ref()),
);
}
}
fn print_all(rows: &[RepoStatus]) {
if rows.is_empty() {
anstream::eprintln!("no worktrees");
return;
}
let repo_w = col_width(rows, |r| r.repo.len(), 4);
let name_w = col_width(rows, |r| r.status.worktree.name.len(), 4);
let branch_w = col_width(
rows,
|r| {
r.status
.worktree
.branch
.as_deref()
.unwrap_or("(detached)")
.len()
},
6,
);
let h = style::DIM;
anstream::println!(
"{h}{:<repo_w$} {:<name_w$} {:<branch_w$} DIRTY UPSTREAM{h:#}",
"REPO",
"NAME",
"BRANCH"
);
let a = style::ACCENT;
for r in rows {
anstream::println!(
"{a}{:<repo_w$}{a:#} {:<name_w$} {:<branch_w$} {} {}",
r.repo,
r.status.worktree.name,
r.status.worktree.branch.as_deref().unwrap_or("(detached)"),
dirty_cell(r.status.dirty_files),
upstream_cell(r.status.upstream.as_ref()),
);
}
}
fn dirty_cell(n: usize) -> String {
if n == 0 {
" ".to_string()
} else {
let s = style::DIRTY;
format!("{s}{n:>5}{s:#}")
}
}
fn upstream_cell(u: Option<&worktree::Upstream>) -> String {
match u {
Some(u) if u.ahead == 0 && u.behind == 0 => u.name.clone(),
Some(u) => {
let ahead = style::AHEAD;
let behind = style::BEHIND;
format!(
"{} {behind}↓{}{behind:#} {ahead}↑{}{ahead:#}",
u.name, u.behind, u.ahead
)
}
None => {
let d = style::DIM;
format!("{d}-{d:#}")
}
}
}