thin-fetch 0.1.0

Low-level Git crate to fetch objects with high granularity
Documentation
use std::{
    io::{Read, Write},
    ops::Deref,
};

use crate::error::Error;

mod decoder;
mod encoder;

pub use decoder::{ParcelDecoder, ParcelItem};
pub use encoder::ParcelEncoder;

const MARKER_SEP: [u8; 4] = [0x30, 0x30, 0x30, 0x31];
const MARKER_END: [u8; 4] = [0x30, 0x30, 0x30, 0x30];

#[derive(Debug, Clone)]
pub struct Parcel {
    pub sections: Vec<Section>,
}

impl Parcel {
    #[must_use]
    pub fn new(sections: Vec<Section>) -> Self {
        Self { sections }
    }

    /// Parse new parcel from a slice of bytes.
    ///
    /// # Errors
    ///
    /// Returns an error on parsing errors.
    pub fn from_bytes(data: &[u8]) -> Result<Self, Error> {
        let dec = ParcelDecoder::new(data);
        dec.collect()
    }

    /// Parse new parcel from a slice of bytes.
    ///
    /// # Errors
    ///
    /// Returns an error on reading or parsing errors.
    pub fn from_reader<R: Read>(reader: R) -> Result<Self, Error> {
        let dec = ParcelDecoder::new(reader);
        dec.collect()
    }

    /// Write parcel to a writer.
    ///
    /// # Errors
    ///
    /// Returns an error on writing or encoding errors.
    pub fn write_to<W: Write>(&self, writer: W) -> Result<(), Error> {
        let mut enc = ParcelEncoder::new(writer);

        let mut first = true;

        for section in &self.sections {
            if first {
                first = false;
            } else {
                enc.new_section()?;
            }

            for data in section.iter() {
                enc.write_data(data)?;
            }
        }

        enc.finish()
    }
}

impl FromIterator<ParcelItem> for Parcel {
    fn from_iter<T: IntoIterator<Item = ParcelItem>>(iter: T) -> Self {
        let mut res = Self::new(vec![]);
        let mut current = Section::new(vec![]);

        for item in iter {
            match item {
                ParcelItem::Data(chunk) => current.chunks.push(chunk),
                ParcelItem::Separator | ParcelItem::End => {
                    res.sections.push(current);
                    current = Section::new(vec![]);
                }
            }
        }

        if !current.is_empty() {
            res.sections.push(current);
        }

        res
    }
}

#[derive(Debug, Clone)]
pub struct Section {
    pub chunks: Vec<Vec<u8>>,
}

impl Section {
    pub fn new(chunks: Vec<Vec<u8>>) -> Self {
        Self { chunks }
    }
}

impl Deref for Section {
    type Target = Vec<Vec<u8>>;

    fn deref(&self) -> &Self::Target {
        &self.chunks
    }
}

impl PartialEq<[&str]> for Section {
    fn eq(&self, other: &[&str]) -> bool {
        if self.chunks.len() != other.len() {
            return false;
        }

        for (a, b) in self.chunks.iter().zip(other) {
            if a != b.as_bytes() {
                return false;
            }
        }

        true
    }
}