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
}
}