use std::collections::HashSet;
use std::path::PathBuf;
use anyhow::bail;
use canonical_path::CanonicalPathBuf;
use clap::Clap;
use libpijul::vertex_buffer::VertexBuffer;
use libpijul::*;
use log::debug;
use crate::repository::Repository;
#[derive(Clap, Debug)]
pub struct Credit {
#[clap(long = "repository")]
repo_path: Option<PathBuf>,
#[clap(long = "channel")]
channel: Option<String>,
file: PathBuf,
}
impl Credit {
pub fn run(self) -> Result<(), anyhow::Error> {
let has_repo_path = self.repo_path.is_some();
let mut repo = Repository::find_root(self.repo_path)?;
let txn = repo.pristine.txn_begin()?;
let channel_name = repo.config.get_current_channel(self.channel.as_ref());
let channel = if let Some(channel) = txn.load_channel(&channel_name)? {
channel
} else {
bail!("No such channel: {:?}", channel_name)
};
if self.channel.is_some() {
repo.config.current_channel = self.channel;
repo.save_config()?;
}
let repo_path = CanonicalPathBuf::canonicalize(&repo.path)?;
let (pos, _ambiguous) = if has_repo_path {
let root = std::fs::canonicalize(repo.path.join(&self.file))?;
let path = root.strip_prefix(&repo_path.as_path())?.to_str().unwrap();
txn.follow_oldest_path(&repo.changes, &channel, &path)?
} else {
let mut root = crate::current_dir()?;
root.push(&self.file);
let root = std::fs::canonicalize(&root)?;
let path = root.strip_prefix(&repo_path.as_path())?.to_str().unwrap();
txn.follow_oldest_path(&repo.changes, &channel, &path)?
};
let channel_ = channel.borrow();
super::pager();
txn.output_file(
&repo.changes,
&channel,
pos,
&mut Creditor::new(std::io::stdout(), &txn, &channel_),
)?;
Ok(())
}
}
pub struct Creditor<'a, W: std::io::Write, T: ChannelTxnT> {
w: W,
buf: Vec<u8>,
new_line: bool,
changes: HashSet<ChangeId>,
txn: &'a T,
channel: &'a T::Channel,
}
impl<'a, W: std::io::Write, T: ChannelTxnT> Creditor<'a, W, T> {
pub fn new(w: W, txn: &'a T, channel: &'a T::Channel) -> Self {
Creditor {
w,
new_line: true,
buf: Vec::new(),
txn,
channel,
changes: HashSet::new(),
}
}
}
impl<'a, W: std::io::Write, T: TxnTExt> VertexBuffer for Creditor<'a, W, T> {
fn output_line<E, C: FnOnce(&mut Vec<u8>) -> Result<(), E>>(
&mut self,
v: Vertex<ChangeId>,
c: C,
) -> Result<(), E>
where
E: From<std::io::Error>,
{
debug!("outputting vertex {:?}", v);
self.buf.clear();
c(&mut self.buf)?;
if !v.change.is_root() {
self.changes.clear();
for e in self
.txn
.iter_adjacent(self.channel, v, EdgeFlags::PARENT, EdgeFlags::all())
.unwrap()
{
let e = e.unwrap();
if e.introduced_by.is_root() {
continue;
}
self.changes.insert(e.introduced_by);
}
if !self.new_line {
writeln!(self.w)?;
}
writeln!(self.w)?;
let mut is_first = true;
for c in self.changes.drain() {
write!(
self.w,
"{}{}",
if is_first { "" } else { ", " },
c.to_base32()
)?;
is_first = false;
}
writeln!(self.w, "\n")?;
}
let ends_with_newline = self.buf.ends_with(b"\n");
if let Ok(s) = std::str::from_utf8(&self.buf[..]) {
for l in s.lines() {
self.w.write_all(b"> ")?;
self.w.write_all(l.as_bytes())?;
self.w.write_all(b"\n")?;
}
}
if !self.buf.is_empty() {
self.new_line = ends_with_newline;
}
Ok(())
}
fn output_conflict_marker(&mut self, s: &str) -> Result<(), std::io::Error> {
if !self.new_line {
self.w.write_all(s.as_bytes())?;
} else {
self.w.write_all(&s.as_bytes()[1..])?;
}
Ok(())
}
}