pub(crate) const REFUSE_ON_TRUNK: &str = "refused: HEAD is on the integration buffer";
const REFS_HEADS_PREFIX: &str = "refs/heads/";
pub(crate) const BASE_CORPUS_STALE: &str =
"refused: fork base predates the authored .doctrine corpus";
pub(crate) const CORPUS_CLOBBER: &str = "refused: advance would clobber authored .doctrine paths";
pub(crate) const DOCTRINE_PATHSPEC: &str = ".doctrine";
pub(crate) const CLOBBER_RENDER_CAP: usize = 20;
use std::collections::BTreeSet;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ClobberReading {
pub path: String,
pub base_oid: Option<String>,
pub new_oid: Option<String>,
}
pub(crate) fn corpus_clobber_check<'a>(
readings: &'a [ClobberReading],
allow: &BTreeSet<String>,
) -> Vec<&'a str> {
readings
.iter()
.filter(|r| r.new_oid == r.base_oid)
.filter(|r| !allow.contains(&r.path))
.map(|r| r.path.as_str())
.collect()
}
pub(crate) fn render_clobbers(paths: &[&str], cap: usize) -> String {
let shown = paths
.iter()
.take(cap)
.copied()
.collect::<Vec<_>>()
.join(", ");
if paths.len() > cap {
format!("{shown} (+{} more)", paths.len() - cap)
} else {
shown
}
}
pub(crate) fn short_branch_name(refish: &str) -> &str {
refish.strip_prefix(REFS_HEADS_PREFIX).unwrap_or(refish)
}
pub(crate) fn on_integration_buffer(
current: Option<&str>,
authoring: Option<&str>,
deliver_to: &str,
) -> bool {
let Some(authoring) = authoring else {
return false; };
if authoring == deliver_to {
return false; }
current == Some(short_branch_name(deliver_to))
}
#[cfg(test)]
mod tests {
use super::*;
fn reading(path: &str, base: Option<&str>, new: Option<&str>) -> ClobberReading {
ClobberReading {
path: path.to_owned(),
base_oid: base.map(str::to_owned),
new_oid: new.map(str::to_owned),
}
}
fn allow(paths: &[&str]) -> BTreeSet<String> {
paths.iter().map(|p| (*p).to_owned()).collect()
}
#[test]
fn phantom_deletion_is_clobber() {
let r = [reading(".doctrine/x.toml", None, None)];
assert_eq!(corpus_clobber_check(&r, &allow(&[])), [".doctrine/x.toml"]);
}
#[test]
fn stale_revert_is_clobber() {
let r = [reading(".doctrine/x.toml", Some("old"), Some("old"))];
assert_eq!(corpus_clobber_check(&r, &allow(&[])), [".doctrine/x.toml"]);
}
#[test]
fn authored_delta_is_not_clobber() {
let r = [reading(".doctrine/x.toml", Some("old"), Some("new"))];
assert!(corpus_clobber_check(&r, &allow(&[])).is_empty());
}
#[test]
fn allowlist_lets_named_path_through() {
let r = [reading(".doctrine/x.toml", Some("old"), Some("old"))];
assert!(corpus_clobber_check(&r, &allow(&[".doctrine/x.toml"])).is_empty());
}
#[test]
fn unnamed_path_still_refused_with_partial_allowlist() {
let r = [
reading(".doctrine/x.toml", Some("old"), Some("old")),
reading(".doctrine/y.toml", None, None),
];
assert_eq!(
corpus_clobber_check(&r, &allow(&[".doctrine/x.toml"])),
[".doctrine/y.toml"]
);
}
#[test]
fn empty_changed_set_is_inert() {
assert!(corpus_clobber_check(&[], &allow(&[])).is_empty());
}
#[test]
fn render_clobbers_lists_under_cap_verbatim() {
assert_eq!(render_clobbers(&["a", "b"], 20), "a, b");
assert_eq!(render_clobbers(&[], 20), "");
}
#[test]
fn render_clobbers_caps_and_summarises_overflow() {
let paths: Vec<&str> = ["p0", "p1", "p2", "p3", "p4"].into();
assert_eq!(render_clobbers(&paths, 2), "p0, p1 (+3 more)");
}
#[test]
fn short_branch_name_strips_refs_heads() {
assert_eq!(short_branch_name("refs/heads/main"), "main");
assert_eq!(short_branch_name("main"), "main");
assert_eq!(short_branch_name("refs/tags/v1"), "refs/tags/v1");
}
#[test]
fn g1_refuses_when_head_on_buffer() {
assert!(on_integration_buffer(
Some("main"),
Some("refs/heads/edge"),
"refs/heads/main"
));
}
#[test]
fn g1_allows_on_authoring_branch() {
assert!(!on_integration_buffer(
Some("edge"),
Some("refs/heads/edge"),
"refs/heads/main"
));
}
#[test]
fn g1_inert_when_posture_unset() {
assert!(!on_integration_buffer(
Some("main"),
None,
"refs/heads/main"
));
}
#[test]
fn g1_inert_when_authoring_equals_deliver_to() {
assert!(!on_integration_buffer(
Some("main"),
Some("refs/heads/main"),
"refs/heads/main"
));
}
#[test]
fn g1_inert_on_detached_head() {
assert!(!on_integration_buffer(
None,
Some("refs/heads/edge"),
"refs/heads/main"
));
}
}