use crate::{lines, App, ToGitnuError, MAX_CACHE_SIZE};
use crate::{Cache, GitnuError};
use std::fs::File;
use std::io::{LineWriter, Write};
use std::process::Stdio;
pub fn uncolor(src: &str) -> Vec<u8> {
let (mut src, mut dst) = (src.as_bytes(), vec![]);
while !src.is_empty() {
match src.iter().position(|v| v == &b'\x1b') {
None => break,
Some(i) => {
dst.extend(&src[..i]);
src = &src[i..];
}
}
match src.iter().position(|v| v == &b'm') {
None => break,
Some(i) => src = &src[i + 1..],
}
}
dst.extend(src);
dst
}
struct State {
seen_untracked: bool,
count: usize,
}
fn normal(state: &mut State, line: String) -> Option<String> {
if state.count > MAX_CACHE_SIZE {
println!("{}", line);
return None;
}
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 line = &uncolor(&line);
let line: &str = std::str::from_utf8(line).unwrap();
let line = line.rsplit_once('\t')?.1;
let (delta, line) = match state.seen_untracked {
true => ("", line),
false => line.split_once(':')?,
};
let line = line.trim_start_matches(|v: char| v.is_ascii_whitespace());
let line = match delta {
"rename" => line
.split_once("->")?
.1
.trim_start_matches(|v: char| v.is_ascii_whitespace()),
_ => line,
};
if line.is_empty() {
return None;
}
Some(line.to_string())
}
fn short(state: &mut State, line: String) -> String {
println!("{: <3}{}", state.count, line);
state.count += 1;
String::from_utf8_lossy(&uncolor(&line))[3..].to_string()
}
trait Writer {
fn write(self, writer: &mut Option<LineWriter<File>>);
}
impl<I: Iterator<Item = String>> Writer for I {
fn write(self, writer: &mut Option<LineWriter<File>>) {
if let Some(writer) = writer.as_mut() {
self.for_each(|v| {
writeln!(writer, "{v}").unwrap();
});
}
}
}
pub fn status(app: &mut App, is_normal: bool) -> Result<(), GitnuError> {
let mut git = app.cmd.stdout(Stdio::piped()).spawn()?;
let lines = match git.stdout.take() {
Some(v) => lines(v),
None => return git.wait().gitnu_err().map(|_| ()),
};
let writer = &mut app.cache_file(true).map(LineWriter::new);
if let Some(lw) = writer {
writeln!(lw, "{}", app.cwd().to_str().unwrap()).unwrap();
}
let state = &mut State { seen_untracked: false, count: 1 };
if is_normal {
lines.filter_map(|v| normal(state, v)).write(writer);
} else {
lines.map(|v| short(state, v)).write(writer);
};
if let Some(lw) = writer {
lw.flush().ok();
}
git.wait().gitnu_err().map(|_| ())
}