gitnu 0.4.2

gitnu indexes your git status so you can use numbers instead of filenames.
Documentation
use crate::{lines, Opts};
use std::io::{LineWriter, Write};
use std::path::PathBuf;
use std::process::Stdio;

const ANSI: [&str; 3] = ["\x1b[31m", "\x1b[32m", "\x1b[m"];

fn uncolor(v: &str) -> String {
    v.replace(ANSI[0], "").replace(ANSI[1], "").replace(ANSI[2], "")
}

fn normal(
    seen_untracked: &mut bool,
    c: &mut usize,
    opts: &Opts,
    line: String,
) -> Option<PathBuf> {
    *seen_untracked |= line.contains("Untracked files:");
    if !line.starts_with('\t') {
        println!("{}", line);
        return None;
    }
    println!("{}{}", c, line);
    *c += 1;
    let f = uncolor(&line.trim_start_matches('\t'));
    let f = match *seen_untracked {
        true => &f,
        false => f.split_once(':')?.1.trim_start(),
    };
    Some(opts.cwd.join(f))
}

fn short(count: &mut usize, opts: &Opts, line: String) -> PathBuf {
    println!("{: <3}{}", count, line);
    *count += 1;
    opts.cwd.join(&uncolor(&line)[3..])
}

pub fn status(mut opts: Opts, is_normal: bool) -> Option<()> {
    let mut git = opts.cmd.stdout(Stdio::piped()).spawn().ok()?;
    let b = lines(git.stdout.as_mut()?);
    let mut writer = opts.cache(true).map(LineWriter::new);
    let write = |line: PathBuf| {
        writer.as_mut().map(|lw| writeln!(lw, "{}", line.display()));
    };
    let count = &mut 1;
    if is_normal {
        let su = &mut false;
        b.filter_map(|v| normal(su, count, &opts, v)).for_each(write);
    } else {
        b.map(|v| short(count, &opts, v)).for_each(write);
    };
    git.wait().ok();
    writer.map(|mut lw| lw.flush().ok()).flatten()
}