Docs

protect

Source: crates/lvcs-ws/src/protect/mod.rs.

/// Whether the file at `path` is protected.
///
/// It is when lvcs-doc's `paths::protected()` (`DEFAULT_PROTECTED`, ASCII
/// case-insensitive) matches `path` with each `/`-separated segment in NFKC
/// and then lowercased (decision 130), because a filesystem that folds
/// Unicode case resolves `build.rſ` (U+017F) or `Cargo.loc` with U+212A
/// (KELVIN SIGN) to the protected file. A segment whose fold is not a name
/// (`.`, `..`, or holding a `/`, as U+2024 ONE DOT LEADER and U+FF0F
/// FULLWIDTH SOLIDUS can make it) keeps its own spelling, since no
/// filesystem resolves it as its fold, so the path keeps its segments and
/// every path the ASCII rule protects stays protected.
#[must_use]
pub fn is_protected(path: &str) -> bool {
    paths::protected().matches(&fold(path))
}

/// What the gate decides one render of a protected path to one mirror from.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GateInput {
    /// The render's heads.
    pub render_heads: Vec<ChangeHash>,
    /// The path's approved heads, or `None` when it has none.
    pub approved: Option<Vec<ChangeHash>>,
    /// The signers of every change beyond the approved heads.
    pub signers_beyond: BTreeSet<ParticipantKey>,
    /// A change beyond the approved heads names no author id
    /// (`ReadAtOutcome::Read.unattributed`, or one the shell can't map).
    pub unattributed: bool,
    /// A change beyond the approved heads carries a bundle in its record
    /// (`ReadAtOutcome::Read.bundled`).
    pub bundled: bool,
    /// The mirror's author.
    pub mirror_author: ParticipantKey,
}

/// What the shell sends the mirror in place of the render.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GateDecision {
    /// The render itself.
    Deliver,
    /// A render at the approved heads, carrying the gated render's `after`.
    Release,
    /// A `Held`, carrying the gated render's `after`.
    Hold,
}

/// Decision 122's gate over one render of a protected path to one mirror.
///
/// `Hold` when `approved` is `None` or `unattributed` is set; `Deliver`
/// when `render_heads` equals `approved` (as sets of heads), or when every
/// signer beyond the approved heads is `mirror_author` and `bundled` is
/// false; `Release` when none of them is `mirror_author`; otherwise `Hold`.
#[must_use]
pub fn gate(input: &GateInput) -> GateDecision {
    let listed = match &input.approved {
        Some(listed) if !input.unattributed => listed,
        _ => return GateDecision::Hold,
    };
    let approved: BTreeSet<&ChangeHash> = listed.iter().collect();
    let render: BTreeSet<&ChangeHash> = input.render_heads.iter().collect();
    let author = &input.mirror_author;
    let authors_own = input.signers_beyond.iter().all(|signer| signer == author);
    if render == approved || (authors_own && !input.bundled) {
        GateDecision::Deliver
    } else if !input.signers_beyond.contains(author) {
        GateDecision::Release
    } else {
        GateDecision::Hold
    }
}