pijul 1.0.0-alpha

The sound distributed version control system.
use super::RemoteRef;
use libpijul::pristine::{Base32, Hash, Merkle, MutTxnT, TxnT};
use libpijul::{MutTxnTExt, TxnTExt};
use std::path::{Path, PathBuf};

pub struct Local {
    pub channel: String,
    pub root: std::path::PathBuf,
    pub changes_dir: std::path::PathBuf,
    pub pristine: libpijul::pristine::sanakirja::Pristine,
    pub name: String,
}

impl Local {
    pub fn get_state(&mut self, mid: Option<u64>) -> Result<Option<(u64, Merkle)>, anyhow::Error> {
        let txn = self.pristine.txn_begin()?;
        let channel = txn.load_channel(&self.channel).unwrap();
        if let Some(mid) = mid {
            Ok(txn.get_changes(&channel, mid).map(|(_, m)| (mid, m)))
        } else {
            Ok(txn
                .reverse_log(&channel.borrow(), None)
                .next()
                .map(|(n, (_, m))| (n, m)))
        }
    }

    pub fn download_changelist<T: MutTxnT>(
        &mut self,
        txn: &mut T,
        remote: &mut RemoteRef<T>,
        from: u64,
        paths: &[String],
    ) -> Result<(), anyhow::Error> {
        let store = libpijul::changestore::filesystem::FileSystem::from_root(&self.root);
        let remote_txn = self.pristine.txn_begin()?;
        let remote_channel = if let Some(channel) = remote_txn.load_channel(&self.channel) {
            channel
        } else {
            debug!("no remote channel named {:?}", self.channel);
            return Ok(());
        };
        let mut paths_ = Vec::new();
        for s in paths {
            if let Ok((p, _ambiguous)) = remote_txn.follow_oldest_path(&store, &remote_channel, s) {
                paths_.push(p)
            }
        }
        for (n, (h, m)) in remote_txn.log(&remote_channel.borrow(), from) {
            if n >= from {
                debug!(
                    "downloading changelist item {:?} {:?} {:?}",
                    n,
                    h.to_base32(),
                    m.to_base32()
                );
                let h_int = remote_txn.get_internal(h).unwrap();
                if paths_.is_empty()
                    || paths_
                        .iter()
                        .any(|x| remote_txn.get_touched_files(*x, Some(h_int)).is_some())
                {
                    txn.put_remote(remote, n, (h, m))?;
                }
            }
        }
        Ok(())
    }

    pub fn upload_changes(
        &mut self,
        mut local: PathBuf,
        to_channel: Option<&str>,
        changes: &[Hash],
    ) -> Result<(), anyhow::Error> {
        let store = libpijul::changestore::filesystem::FileSystem::from_root(&self.root);
        let mut txn = self.pristine.mut_txn_begin();
        let mut channel = txn.open_or_create_channel(to_channel.unwrap_or(&self.channel))?;
        let mut ws = libpijul::ApplyWorkspace::new();
        for c in changes {
            libpijul::changestore::filesystem::push_filename(&mut local, &c);
            libpijul::changestore::filesystem::push_filename(&mut self.changes_dir, &c);
            std::fs::create_dir_all(&self.changes_dir.parent().unwrap())?;
            debug!("hard link {:?} {:?}", local, self.changes_dir);
            std::fs::hard_link(&local, &self.changes_dir)?;
            debug!("hard link done");
            libpijul::changestore::filesystem::pop_filename(&mut local);
            libpijul::changestore::filesystem::pop_filename(&mut self.changes_dir);
            txn.apply_change_ws(&store, &mut channel, *c, &mut ws)?;
        }
        let mut repo = libpijul::working_copy::filesystem::FileSystem::from_root(&self.root);
        txn.output_repository_no_pending(&mut repo, &store, &mut channel, "", true)?;
        txn.commit()?;
        Ok(())
    }

    pub async fn start_change_download(
        &mut self,
        c: libpijul::pristine::Hash,
        path: &Path,
    ) -> Result<(), anyhow::Error> {
        libpijul::changestore::filesystem::push_filename(&mut self.changes_dir, &c);
        debug!("hard link {:?} {:?}", self.changes_dir, path);
        std::fs::hard_link(&self.changes_dir, path)?;
        debug!("hard link done");
        libpijul::changestore::filesystem::pop_filename(&mut self.changes_dir);
        debug!("sent");
        Ok(())
    }
}