srcdmp 0.1.0-alpha.1

Amazing code snapshot tool and library
Documentation
use crate::raw::error::SdmpError;

/// A single file entry inside a `.srcdmp` archive.
///
/// Each entry is a per-file header that precedes the file's encoded
/// content in the archive body. It contains the relative path and
/// the byte length of the encoded data that follows.
///
/// # Wire Format
///
/// | Offset | Size | Field |
/// | :--- | :--- | :--- |
/// | 0 | 2 | Path length (little-endian `u16`) |
/// | 2 | N | UTF-8 path bytes |
/// | 2+N | 4 | Encoded file size (little-endian `u32`) |
///
/// # Examples
///
/// ```rust
/// use srcdmp::raw::entry::Entry;
///
/// let entry = Entry {
///     path: "src/main.rs".to_string(),
///     file_size: 932,
/// };
///
/// let mut buf = Vec::new();
/// entry.write(&mut buf);
///
/// let (decoded, consumed) = Entry::read(&buf).unwrap();
/// assert_eq!(decoded.path, "src/main.rs");
/// assert_eq!(decoded.file_size, 932);
/// assert_eq!(consumed, 17); // 2 + 11 + 4
/// ```
pub struct Entry {
    /// Relative path from the project root, e.g. `"src/main.rs"`.
    pub path: String,
    /// Number of encoded bytes that follow this entry header.
    pub file_size: u32,
}

impl Entry {
    /// Serialize this entry header into a byte buffer.
    ///
    /// Appends the path length (as a little-endian `u16`), the path
    /// as UTF-8 bytes, and the file size (as a little-endian `u32`).
    pub fn write(&self, output: &mut Vec<u8>) {
        let path_bytes = self.path.as_bytes();
        let path_len = path_bytes.len() as u16;
        output.extend_from_slice(&path_len.to_le_bytes());

        output.extend_from_slice(path_bytes);

        output.extend_from_slice(&self.file_size.to_le_bytes());
    }

    /// Deserialize an entry header from a byte slice.
    ///
    /// Returns the entry and the number of bytes consumed.
    ///
    /// # Errors
    ///
    /// Returns [`SdmpError::UnexpectedEof`] if the slice is too short
    /// to contain the path length, path, or file size fields.
    /// Returns [`SdmpError::InvalidUtf8`] if the path bytes are not
    /// valid UTF-8.
    pub fn read(bytes: &[u8]) -> Result<(Self, usize), SdmpError> {
        let mut i = 0;

        if bytes.len() < 2 {
            return Err(SdmpError::UnexpectedEof);
        }

        let path_len = u16::from_le_bytes([bytes[i], bytes[i + 1]]) as usize;
        i += 2;

        if bytes.len() < i + path_len {
            return Err(SdmpError::UnexpectedEof);
        }
        let path = String::from_utf8(bytes[i..i + path_len].to_vec())
            .map_err(|_| SdmpError::InvalidUtf8)?;
        i += path_len;

        if bytes.len() < i + 4 {
            return Err(SdmpError::UnexpectedEof);
        }
        let file_size = u32::from_le_bytes([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]]);
        i += 4;

        Ok((Self { path, file_size }, i))
    }
}