libvctrl 0.5.1

A robust, content-addressed version control engine for arbitrary data, designed for embedding into applications.
Documentation
use crate::codec::Encoder;
use crate::command::Command;
use crate::domain::commit::Commit;
use crate::domain::hash::Hash;
use crate::domain::object::Object;
use crate::domain::user::UserID;
use crate::error::VctrlError;
use crate::hashing::Hasher;
use crate::storage::traits::{ObjectStore, ObjectStoreExt, RefStore};

pub struct StashPush {
    pub tree_hash: Hash,
    pub author: UserID,
    pub message: Option<String>,
    pub encoder: Box<dyn Encoder>,
    pub hasher: Box<dyn Hasher>,
}

impl Command for StashPush {
    type Output = Hash;

    fn execute(
        &self,
        store: &mut dyn ObjectStore,
        refs: &mut dyn RefStore,
    ) -> Result<Hash, VctrlError> {
        let msg = self.message.clone().unwrap_or_else(|| "stash".to_string());
        let commit = Commit::new(
            self.tree_hash,
            vec![],
            self.author.clone(),
            self.author.clone(),
            msg,
            None,
        );
        let mut buf = Vec::new();
        self.encoder.encode_commit(&commit, &mut buf)?;
        let hash = self.hasher.hash_commit_encoded(&buf);
        store.put(&hash, &Object::Commit(Box::new(commit)))?;

        let nanos = chrono::Utc::now()
            .timestamp_nanos_opt()
            .ok_or_else(|| VctrlError::Other("invalid system clock".into()))?;
        let ref_name = format!("refs/stash/{}", nanos);
        refs.set_ref(&ref_name, &hash)?;
        Ok(hash)
    }
}

pub struct StashPop;

impl Command for StashPop {
    type Output = Option<Hash>;

    fn execute(
        &self,
        store: &mut dyn ObjectStore,
        refs: &mut dyn RefStore,
    ) -> Result<Option<Hash>, VctrlError> {
        let mut stash_refs = refs.list_refs("refs/stash/")?;
        if stash_refs.is_empty() {
            return Ok(None);
        }
        stash_refs.sort();
        let last = stash_refs
            .pop()
            .ok_or_else(|| VctrlError::Other("no stash entries".into()))?;
        let commit_hash = refs
            .get_ref(&last)?
            .ok_or_else(|| VctrlError::NotFound(format!("stash ref '{}' missing", last)))?;
        let commit = store.get_commit(&commit_hash)?;
        refs.delete_ref(&last)?;
        Ok(Some(commit.tree))
    }
}

pub struct StashList;

impl Command for StashList {
    type Output = Vec<(String, Hash)>;

    fn execute(
        &self,
        _store: &mut dyn ObjectStore,
        refs: &mut dyn RefStore,
    ) -> Result<Vec<(String, Hash)>, VctrlError> {
        let stash_refs = refs.list_refs("refs/stash/")?;
        let mut result = Vec::new();
        for r in stash_refs {
            if let Some(hash) = refs.get_ref(&r)? {
                result.push((r, hash));
            }
        }
        Ok(result)
    }
}