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())
}
pub fn status(app: &mut App, is_normal: bool) {
inner(app, is_normal);
}