git_rs 0.1.0

A (work in progress) pure rust git implementation.
Documentation
use std::collections::HashSet;
use std::io::Cursor;
use std::io::Write;

use crate::walk::commits::CommitIterator;
use crate::walk::tree::TreeIterator;
use crate::objects::{Type, Object};
use crate::errors::Result;
use crate::id::Id;

pub mod loose;
pub mod pack;
pub mod fs;

pub trait Queryable {
    fn get<W: Write, S: Queryable>(&self, id: &Id, output: &mut W, backends: &StorageSet<S>) -> Result<Option<Type>>;
}

impl Queryable for () {
    fn get<W: Write, S: Queryable>(&self, id: &Id, _output: &mut W, _backends: &StorageSet<S>) -> Result<Option<Type>> {
        Ok(None)
    }
}

impl<Q: Queryable> Queryable for (Q,) {
    fn get<W: Write, S: Queryable>(&self, id: &Id, output: &mut W, backends: &StorageSet<S>) -> Result<Option<Type>> {
        self.0.get(id, output, backends)
    }
}

impl<H: Queryable, T: Queryable> Queryable for (H, T) {
    fn get<W: Write, S: Queryable>(&self, id: &Id, output: &mut W, backends: &StorageSet<S>) -> Result<Option<Type>> {
        let opt = self.0.get(id, output, backends)?;
        if opt.is_some() {
            return Ok(opt)
        }

        self.1.get(id, output, backends)
    }
}

impl<Q: Queryable> Queryable for Vec<Q> {
    fn get<W: Write, S: Queryable>(&self, id: &Id, output: &mut W, backends: &StorageSet<S>) -> Result<Option<Type>> {
        for queryable in self {
            let opt = queryable.get(id, output, backends)?;
            if opt.is_some() {
                return Ok(opt)
            }
        }

        Ok(None)
    }
}

pub struct StorageSet<Q: Queryable> {
    backend: Q
}

impl<Q: Queryable> StorageSet<Q> {
    pub fn new(backend: Q) -> StorageSet<Q> {
        StorageSet {
            backend
        }
    }

    pub fn get<W: Write>(&self, id: &Id, output: &mut W) -> Result<Option<Type>> {
        self.backend.get(id, output, &self)
    }

    pub fn commits(&self, id: &Id, seen: Option<HashSet<Id>>) -> CommitIterator<Q> {
        CommitIterator::new(&self, id, seen)
    }

    pub fn tree(&self, id: &Id) -> TreeIterator<Q> {
        let result = match self.get_and_load(id) {
            Ok(xs) => xs,
            Err(_) => return TreeIterator::new(&self, vec![])
        };

        if result.is_none() {
            return TreeIterator::new(&self, vec![])
        }

        match result.unwrap() {
            Object::Commit(commit) => {
                match commit.tree() {
                    Some(tree) => self.tree(&tree),
                    None => TreeIterator::new(&self, vec![])
                }
            },
            Object::Tree(tree) => {
                TreeIterator::new(&self, vec![tree.into_iter()])
            },
            _ => {
                TreeIterator::new(&self, vec![])
            }
        }
    }

    pub fn get_and_load(&self, id: &Id) -> Result<Option<Object>> {
        let mut data = Vec::new();
        match self.get(id, &mut data)? {
            Some(typ) => Ok(Some(typ.load(&mut Cursor::new(&data))?)),
            None => Ok(None)
        }
    }
}