libvctrl 0.5.1

A robust, content-addressed version control engine for arbitrary data, designed for embedding into applications.
Documentation
use crate::domain::hash::Hash;
use crate::domain::object::Object;
use crate::error::VctrlError;
use crate::reflog::ReflogEntry;
use crate::storage::traits::{ObjectStore, RefStore};
use std::collections::HashMap;

pub struct MemoryStore {
    objects: HashMap<Hash, Object>,
}

impl MemoryStore {
    pub fn new() -> Self {
        Self {
            objects: HashMap::new(),
        }
    }
}
impl Default for MemoryStore {
    fn default() -> Self {
        Self::new()
    }
}
impl ObjectStore for MemoryStore {
    fn put(&mut self, hash: &Hash, obj: &Object) -> Result<(), VctrlError> {
        self.objects.insert(*hash, obj.clone());
        Ok(())
    }
    fn get(&self, hash: &Hash) -> Result<Option<Object>, VctrlError> {
        Ok(self.objects.get(hash).cloned())
    }
    fn exists(&self, hash: &Hash) -> Result<bool, VctrlError> {
        Ok(self.objects.contains_key(hash))
    }
    fn all_hashes(&self) -> Result<Vec<Hash>, VctrlError> {
        Ok(self.objects.keys().copied().collect())
    }
    fn remove(&mut self, hash: &Hash) -> Result<(), VctrlError> {
        self.objects.remove(hash);
        Ok(())
    }
}

pub struct MemoryRefStore {
    refs: HashMap<String, Hash>,
    head: Option<String>,
    reflog: Vec<ReflogEntry>,
}

impl MemoryRefStore {
    pub fn new() -> Self {
        Self {
            refs: HashMap::new(),
            head: None,
            reflog: Vec::new(),
        }
    }
}
impl Default for MemoryRefStore {
    fn default() -> Self {
        Self::new()
    }
}

impl RefStore for MemoryRefStore {
    fn set_ref(&mut self, name: &str, hash: &Hash) -> Result<(), VctrlError> {
        let old = self.refs.get(name).copied();
        self.refs.insert(name.to_string(), *hash);
        self.reflog.push(ReflogEntry {
            ref_name: name.to_string(),
            old_hash: old,
            new_hash: *hash,
            timestamp: chrono::Utc::now(),
            message: format!("set_ref {}", name),
        });
        Ok(())
    }
    fn get_ref(&self, name: &str) -> Result<Option<Hash>, VctrlError> {
        Ok(self.refs.get(name).copied())
    }
    fn delete_ref(&mut self, name: &str) -> Result<(), VctrlError> {
        self.refs.remove(name);
        Ok(())
    }
    fn set_head(&mut self, target: &str) -> Result<(), VctrlError> {
        self.head = Some(target.to_string());
        Ok(())
    }
    fn head(&self) -> Result<Option<Hash>, VctrlError> {
        match &self.head {
            Some(target) if target.starts_with("refs/") => self.get_ref(target),
            Some(direct) => Hash::from_hex(direct).map(Some).map_err(VctrlError::Hash),
            None => Ok(None),
        }
    }
    fn head_ref_name(&self) -> Result<Option<String>, VctrlError> {
        match &self.head {
            Some(target) if target.starts_with("refs/heads/") => {
                if self.refs.contains_key(target) {
                    Ok(Some(target.clone()))
                } else {
                    Ok(None)
                }
            }
            _ => Ok(None),
        }
    }
    fn list_refs(&self, prefix: &str) -> Result<Vec<String>, VctrlError> {
        Ok(self
            .refs
            .keys()
            .filter(|k| k.starts_with(prefix))
            .cloned()
            .collect())
    }
}

impl crate::reflog::ReflogStore for MemoryRefStore {
    fn log_ref_update(
        &mut self,
        ref_name: &str,
        old_hash: Option<Hash>,
        new_hash: Hash,
        message: &str,
    ) -> Result<(), VctrlError> {
        self.reflog.push(ReflogEntry {
            ref_name: ref_name.to_string(),
            old_hash,
            new_hash,
            timestamp: chrono::Utc::now(),
            message: message.to_string(),
        });
        Ok(())
    }
    fn reflog(&self, ref_name: &str) -> Result<Vec<ReflogEntry>, VctrlError> {
        Ok(self
            .reflog
            .iter()
            .filter(|e| e.ref_name == ref_name)
            .cloned()
            .collect())
    }
}