use crate::entity::Kind;
use crate::knowledge::RecordKind;
#[derive(Copy, Clone)]
pub(crate) enum StorageTarget {
RelationRow,
TypedArray { field: &'static str },
}
#[derive(Copy, Clone)]
pub(crate) struct SupersedePolicy {
pub(crate) storage: StorageTarget,
pub(crate) carveout_field: &'static str,
pub(crate) superseded_status: &'static str,
}
pub(crate) fn supersede_policy(kind: &Kind) -> Option<SupersedePolicy> {
match kind.prefix {
"ADR" | "POL" | "STD" => Some(SupersedePolicy {
storage: StorageTarget::RelationRow,
carveout_field: "superseded_by",
superseded_status: "superseded",
}),
"ASM" | "QUE" => Some(SupersedePolicy {
storage: StorageTarget::TypedArray {
field: "supersedes",
},
carveout_field: "superseded_by",
superseded_status: "obsolete",
}),
"DEC" | "CON" => Some(SupersedePolicy {
storage: StorageTarget::TypedArray {
field: "supersedes",
},
carveout_field: "superseded_by",
superseded_status: "superseded",
}),
_ => None,
}
}
pub(crate) fn validate_matrix(new: RecordKind, old: RecordKind) -> bool {
use RecordKind::{Assumption, Constraint, Decision, Question};
#[expect(clippy::unnested_or_patterns, reason = "clear matrix representation")]
{
matches!(
(old, new),
(Assumption, Assumption | Decision | Constraint)
| (Question, Question | Decision | Constraint | Assumption)
| (Decision, Decision | Constraint)
| (Constraint, Constraint | Decision)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::knowledge::RecordKind::*;
#[test]
fn validate_matrix_assumption_predecessor() {
assert!(validate_matrix(Assumption, Assumption));
assert!(validate_matrix(Decision, Assumption));
assert!(validate_matrix(Constraint, Assumption));
assert!(!validate_matrix(Question, Assumption));
}
#[test]
fn validate_matrix_question_predecessor() {
assert!(validate_matrix(Question, Question));
assert!(validate_matrix(Decision, Question));
assert!(validate_matrix(Constraint, Question));
assert!(validate_matrix(Assumption, Question));
}
#[test]
fn validate_matrix_decision_predecessor() {
assert!(validate_matrix(Decision, Decision));
assert!(validate_matrix(Constraint, Decision));
assert!(!validate_matrix(Assumption, Decision));
assert!(!validate_matrix(Question, Decision));
}
#[test]
fn validate_matrix_constraint_predecessor() {
assert!(validate_matrix(Constraint, Constraint));
assert!(validate_matrix(Decision, Constraint));
assert!(!validate_matrix(Assumption, Constraint));
assert!(!validate_matrix(Question, Constraint));
}
fn governance_kind(prefix: &str) -> &'static crate::entity::Kind {
for kref in crate::integrity::KINDS {
if kref.kind.prefix == prefix {
return kref.kind;
}
}
panic!("no kind with prefix {prefix}");
}
#[test]
fn supersede_policy_returns_some_for_pol() {
let kind = governance_kind("POL");
let policy = supersede_policy(kind).unwrap();
assert_eq!(policy.carveout_field, "superseded_by");
assert_eq!(policy.superseded_status, "superseded");
assert!(matches!(policy.storage, StorageTarget::RelationRow));
}
#[test]
fn supersede_policy_returns_some_for_std() {
let kind = governance_kind("STD");
let policy = supersede_policy(kind).unwrap();
assert_eq!(policy.carveout_field, "superseded_by");
assert_eq!(policy.superseded_status, "superseded");
assert!(matches!(policy.storage, StorageTarget::RelationRow));
}
#[test]
fn supersede_policy_returns_some_for_adr() {
let kind = governance_kind("ADR");
let policy = supersede_policy(kind).unwrap();
assert_eq!(policy.superseded_status, "superseded");
assert!(matches!(policy.storage, StorageTarget::RelationRow));
}
#[test]
fn supersede_policy_storage_is_relation_row_for_governance() {
for prefix in ["ADR", "POL", "STD"] {
let kind = governance_kind(prefix);
let policy = supersede_policy(kind).unwrap();
assert!(
matches!(policy.storage, StorageTarget::RelationRow),
"{prefix} should use RelationRow"
);
}
}
#[test]
fn supersede_policy_storage_is_typed_array_for_records() {
for (prefix, expected_status) in [
("ASM", "obsolete"),
("DEC", "superseded"),
("QUE", "obsolete"),
("CON", "superseded"),
] {
let kind = governance_kind(prefix);
let policy = supersede_policy(kind).unwrap();
assert_eq!(policy.superseded_status, expected_status);
assert!(
matches!(
policy.storage,
StorageTarget::TypedArray {
field: "supersedes"
}
),
"{prefix} should use TypedArray"
);
}
}
#[test]
fn supersede_policy_returns_none_for_unsupported_kinds() {
for prefix in ["SL", "IMP", "RV", "PRD", "REQ", "RSK"] {
let kind = governance_kind(prefix);
assert!(supersede_policy(kind).is_none(), "{prefix} should be None");
}
}
}