git-closure 0.1.0

Deterministic, self-describing, verifiable source-tree snapshots
Documentation
/// Core snapshot types shared across the build, serialization, hash, and
/// materialize layers.  Deliberately kept as a types-only module to keep
/// the dependency graph acyclic.
pub mod build;
pub mod diff;
pub mod hash;
pub mod render;
pub mod serial;
pub mod summary;

use crate::error::GitClosureError;

pub(crate) type Result<T> = std::result::Result<T, GitClosureError>;

/// An individual file record within a `.gcl` snapshot.
///
/// Invariants:
/// - `symlink_target.is_some()` ⟺ `sha256.is_empty() && content.is_empty() && mode == "120000"`
/// - `encoding == Some("base64")` ⟺ `content` contains non-UTF-8 bytes
/// - `content.len() as u64 == size` for all regular files
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SnapshotFile {
    pub path: String,
    pub sha256: String,
    pub mode: String,
    pub size: u64,
    pub encoding: Option<String>,
    pub symlink_target: Option<String>,
    pub content: Vec<u8>,
}

/// Parsed representation of the `;;`-comment header block at the top of a
/// `.gcl` file.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct SnapshotHeader {
    pub snapshot_hash: String,
    pub file_count: usize,
    /// SHA-1 / SHA-256 revision captured from the source git repository at
    /// build time.  Informational only — not included in `snapshot_hash`.
    pub git_rev: Option<String>,
    /// Short branch name captured from the source git repository at build time.
    /// Informational only — not included in `snapshot_hash`.
    pub git_branch: Option<String>,
    /// Unknown `;; key: value` comments preserved for forward compatibility.
    pub extra_headers: Vec<(String, String)>,
}

/// Options that influence which files are included in a snapshot build.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BuildOptions {
    /// Include files not tracked by git (mirrors `git ls-files --others`).
    pub include_untracked: bool,
    /// Abort the build if the working tree is dirty (uncommitted changes).
    pub require_clean: bool,
    /// Optional provenance annotation for remote-origin snapshots.
    ///
    /// Stored as `(source_uri, source_provider)` and serialized into header
    /// comments as `source-uri` and `source-provider`.
    pub source_annotation: Option<(String, String)>,
}

/// Summary returned by [`crate::verify_snapshot`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VerifyReport {
    pub file_count: usize,
}

/// A single entry returned by [`crate::list_snapshot`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ListEntry {
    /// Slash-delimited path relative to the snapshot root.
    pub path: String,
    /// `true` for symlinks, `false` for regular files.
    pub is_symlink: bool,
    /// Symlink target string (only set when `is_symlink == true`).
    pub symlink_target: Option<String>,
    /// Hex-encoded SHA-256 of file content (empty for symlinks).
    pub sha256: String,
    /// Octal permission bits as a string (e.g. `"644"`, `"755"`, `"120000"`).
    pub mode: String,
    /// Byte size of file content (0 for symlinks).
    pub size: u64,
}

/// Aggregated metadata derived from a snapshot file.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub struct SnapshotSummary {
    pub snapshot_hash: String,
    pub file_count: usize,
    pub regular_count: usize,
    pub symlink_count: usize,
    pub total_bytes: u64,
    pub git_rev: Option<String>,
    pub git_branch: Option<String>,
    pub largest_files: Vec<(String, u64)>,
}