pijul 1.0.0-alpha

The sound distributed version control system.
use crate::repository::*;
use libanu::change::*;
use libanu::pristine::MutTxnT;
use std::collections::BTreeMap;
use std::path::PathBuf;

#[derive(Clap, Debug)]
pub struct Diff {
    #[clap(long = "repository")]
    pub repo_path: Option<PathBuf>,
    #[clap(long = "json")]
    pub json: bool,
    #[clap(long = "channel")]
    pub channel: Option<String>,
    #[clap(long = "tag")]
    pub tag: bool,
    pub prefixes: Vec<PathBuf>,
}

impl Diff {
    pub fn run(self) -> Result<(), anyhow::Error> {
        let mut repo = Repository::find_root(self.repo_path.clone())?;
        let mut txn = repo.pristine.mut_txn_begin();
        let mut channel =
            txn.open_or_create_channel(repo.config.get_current_channel(self.channel.as_ref()))?;

        let mut state = libanu::RecordBuilder::new();
        repo.working_copy.record_prefixes(
            &mut txn,
            &mut channel,
            &repo.changes,
            &mut state,
            &repo.path,
            &self.prefixes,
        )?;
        let rec = state.finish();
        if rec.actions.is_empty() {
            return Ok(());
        }
        let actions = rec
            .actions
            .into_iter()
            .map(|rec| rec.globalize(&txn))
            .collect();
        let mut change = LocalChange3::make_change(
            &txn,
            &channel,
            actions,
            rec.contents,
            ChangeHeader::default(),
            Vec::new(),
        );

        let (dependencies, extra_known) = if self.tag {
            full_dependencies(&txn, &channel)
        } else {
            dependencies(&txn, &channel, change.changes.iter())
        };
        change.dependencies = dependencies;
        change.extra_known = extra_known;

        if self.json {
            let mut changes = BTreeMap::new();
            for ch in change.changes.iter() {
                changes.entry(ch.path()).or_insert(Vec::new()).push(Status {
                    operation: match ch {
                        Record::FileMove { .. } => "file move",
                        Record::FileDel { .. } => "file del",
                        Record::FileUndel { .. } => "file undel",
                        Record::SolveNameConflict { .. } => "solve name conflict",
                        Record::UnsolveNameConflict { .. } => "unsolve name conflict",
                        Record::FileAdd { .. } => "file add",
                        Record::Edit { .. } => "edit",
                        Record::Replacement { .. } => "replacement",
                        Record::SolveOrderConflict { .. } => "solve order conflict",
                        Record::UnsolveOrderConflict { .. } => "unsolve order conflict",
                        Record::ResurrectZombies { .. } => "resurrect zombies",
                    },
                    line: ch.line(),
                });
            }
            serde_json::to_writer_pretty(&mut std::io::stdout(), &changes)?;
            println!("");
        } else {
            change.write(
                &repo.changes,
                None,
                |local: &libanu::change::Local, _| -> String {
                    format!("{}:{}", local.path, local.line)
                },
                true,
                &mut std::io::stdout(),
            )?
        }
        Ok(())
    }
}

#[derive(Debug, Serialize)]
struct Status {
    operation: &'static str,
    line: Option<usize>,
}