use std::fmt::Write;
use std::io;
use std::path::Path;
use crate::civil::iso8601;
use crate::git;
const FIELD: char = '\u{1f}';
const RECORD: char = '\u{1e}';
pub(crate) fn section(store: &Path, id: &str) -> io::Result<String> {
let path = format!("tasks/{id}.md");
let fmt = format!(
"--format={RECORD}%ct{FIELD}%an{FIELD}%(trailers:key=bl-op,valueonly=true){FIELD}\
%(trailers:key=bl-actor,valueonly=true){FIELD}%(trailers){FIELD}%b"
);
let log = git::run(store, &["log", "--reverse", &fmt, "--", &path], None)?;
let mut out = String::new();
for record in log.split(RECORD).skip(1) {
entry(&mut out, record);
}
if !out.is_empty() {
out.insert_str(0, " journal\n");
}
Ok(out)
}
fn entry(out: &mut String, record: &str) {
let mut f = record.splitn(6, FIELD);
let ct = f.next().expect("splitn always yields a first field");
let ct: i64 = ct.parse().expect("git %ct is an integer unix timestamp");
let author = f.next().unwrap_or_default();
let op = first_line(f.next().unwrap_or_default());
let actor = first_line(f.next().unwrap_or_default());
let trailers = f.next().unwrap_or_default();
let body = f.next().unwrap_or_default();
let actor = if actor.is_empty() { author.trim() } else { actor };
let _ = writeln!(out, " {} {:<9}{}", iso8601(ct), op, actor);
let note = body.trim_end();
let note = note.strip_suffix(trailers.trim_end()).unwrap_or(note).trim();
for line in note.lines() {
let _ = if line.is_empty() { writeln!(out) } else { writeln!(out, " {line}") };
}
}
fn first_line(s: &str) -> &str {
s.lines().next().unwrap_or_default().trim()
}
#[cfg(test)]
#[path = "journal_tests.rs"]
mod tests;