gitnu 0.4.8

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

struct State {
    seen_untracked: bool,
    count: usize,
}

fn normal(state: &mut State, path: &PathBuf, line: String) -> Option<PathBuf> {
    state.seen_untracked |= line.starts_with("Untracked files:");
    if !line.starts_with('\t') {
        println!("{}", line);
        return None;
    }
    println!("{}{}", state.count, line);
    state.count += 1;
    let mut line: &[u8] = &uncolor(&line);
    let line = &mut line;
    line.after_last(b'\t');
    let is_rename = line.starts_with(b"renamed:");
    if !state.seen_untracked {
        line.after_last(b':');
    }
    line.trim_left(b' ');
    if is_rename {
        line.after_first_sequence(b"->");
        line.trim_left_while(|v| v.is_ascii_whitespace());
    }
    if line.is_empty() {
        return None;
    }
    std::str::from_utf8(line).map(|v| path.join(v)).ok()
}

fn short(state: &mut State, path: &PathBuf, line: String) -> PathBuf {
    println!("{: <3}{}", state.count, line);
    state.count += 1;
    let line = String::from_utf8(uncolor(&line)).unwrap();
    path.join(&line[3..])
}

trait Writer {
    fn write(self, writer: &mut Option<LineWriter<File>>);
}

impl<I: Iterator<Item = PathBuf>> Writer for I {
    fn write(self, writer: &mut Option<LineWriter<File>>) {
        if let Some(writer) = writer.as_mut() {
            self.for_each(|v| {
                v.to_str().map(|v| writeln!(writer, "{v}"));
            });
        }
    }
}

fn inner(app: &mut App, is_normal: bool) -> Option<()> {
    let mut git = app.cmd.stdout(Stdio::piped()).spawn().ok()?;
    let lines = lines(git.stdout.as_mut()?);
    let writer = &mut app.cache(true).map(LineWriter::new);
    let state = &mut State { seen_untracked: false, count: 1 };
    if is_normal {
        lines.filter_map(|v| normal(state, &app.cwd, v)).write(writer);
    } else {
        lines.map(|v| short(state, &app.cwd, v)).write(writer);
    };
    git.wait().ok();
    writer.as_mut().and_then(|v| v.flush().ok())
}

/// Endpoint function for everything git-status related.
///
/// Runs `git status` then parses its output, enumerates it, and
/// prints it out to stdout.
pub fn status(app: &mut App, is_normal: bool) {
    inner(app, is_normal);
}