1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::fmt;
/// Durable role of a named branch.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BranchKind {
/// A component namespace: a permanent isolation boundary that never merges
/// away into another branch.
Namespace,
/// A mutable work branch. Pre-marker ref records decode to this permissive
/// compatibility default.
Work,
}
impl fmt::Display for BranchKind {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Namespace => formatter.write_str("Namespace"),
Self::Work => formatter.write_str("Work"),
}
}
}
/// Typed engine refusals at namespace boundaries.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BranchPolicyError {
/// A fork would change an existing Namespace lineage.
ForkAcrossNamespaceBoundary {
/// Durable source branch name.
source: String,
/// Durable source branch kind.
source_kind: BranchKind,
/// Namespace lineage resolved from the source ref record.
source_lineage: Option<String>,
/// Durable destination branch name.
destination: String,
/// Durable destination branch kind.
destination_kind: BranchKind,
/// Namespace lineage the destination record would resolve to.
destination_lineage: Option<String>,
},
/// A merge would move content between different Namespace lineages.
MergeAcrossNamespaceBoundary {
/// Durable source branch name.
source: String,
/// Durable source branch kind.
source_kind: BranchKind,
/// Namespace lineage resolved from the source ref record.
source_lineage: Option<String>,
/// Durable destination branch name.
destination: String,
/// Durable destination branch kind.
destination_kind: BranchKind,
/// Namespace lineage resolved from the destination ref record.
destination_lineage: Option<String>,
},
/// A Namespace branch was supplied as a merge source.
NamespaceMerge {
/// Durable source branch name.
source: String,
/// Durable source branch kind.
source_kind: BranchKind,
/// Durable destination branch name.
destination: String,
/// Durable destination branch kind.
destination_kind: BranchKind,
},
}
impl fmt::Display for BranchPolicyError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ForkAcrossNamespaceBoundary {
source,
source_kind,
source_lineage,
destination,
destination_kind,
destination_lineage,
} => write!(
formatter,
"policy refused fork: '{source}' ({source_kind}, lineage {}) into \
'{destination}' ({destination_kind}, lineage {})",
Lineage(source_lineage.as_deref()),
Lineage(destination_lineage.as_deref()),
),
Self::MergeAcrossNamespaceBoundary {
source,
source_kind,
source_lineage,
destination,
destination_kind,
destination_lineage,
} => write!(
formatter,
"policy refused merge: '{source}' ({source_kind}, lineage {}) into \
'{destination}' ({destination_kind}, lineage {})",
Lineage(source_lineage.as_deref()),
Lineage(destination_lineage.as_deref()),
),
Self::NamespaceMerge {
source,
source_kind,
destination,
destination_kind,
} => write!(
formatter,
"policy refused merge: '{source}' ({source_kind}) into \
'{destination}' ({destination_kind}); a Namespace branch never merges away",
),
}
}
}
impl std::error::Error for BranchPolicyError {}
struct Lineage<'a>(Option<&'a str>);
impl fmt::Display for Lineage<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Some(lineage) => formatter.write_str(lineage),
None => formatter.write_str("none"),
}
}
}