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
//! Stable refusal classifications emitted by operator-facing command surfaces.
//!
//! The token spellings are a machine contract. This vocabulary is closed (only
//! the variants below are accepted) and append-only: variants may be added, but
//! an existing token must never be renamed or reused for a different meaning.
use core::fmt;
use crate::UnknownVariant;
/// Stable reason attached to a refused `kkernel exec` invocation or operation.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[non_exhaustive]
pub enum RefusalReason {
/// The resolved actor was anonymous where attribution was required.
AnonymousActor,
/// The resolved actor did not match `--expect-actor`.
ExpectActorMismatch,
/// A write was refused by the content secret gate.
GateRefusal,
/// `--strict` observed at least one failed or aborted operation.
StrictOpFailure,
/// The supplied operation expression could not be parsed.
ParseError,
/// The requested verb was unknown or was not loaded.
VerbRefused,
/// A write was refused by the immutable stream record policy.
PolicyRefusal,
}
impl RefusalReason {
/// Every currently defined reason, in documentation order.
pub const ALL: [Self; 7] = [
Self::AnonymousActor,
Self::ExpectActorMismatch,
Self::GateRefusal,
Self::StrictOpFailure,
Self::ParseError,
Self::VerbRefused,
Self::PolicyRefusal,
];
/// Exact machine token written to stderr and JSON envelopes.
pub const fn as_str(self) -> &'static str {
match self {
Self::AnonymousActor => "anonymous-actor",
Self::ExpectActorMismatch => "expect-actor-mismatch",
Self::GateRefusal => "gate-refusal",
Self::StrictOpFailure => "strict-op-failure",
Self::ParseError => "parse-error",
Self::VerbRefused => "verb-refused",
Self::PolicyRefusal => "policy-refusal",
}
}
/// Parse one exact machine token without accepting aliases or case folding.
pub fn from_token(token: &str) -> Option<Self> {
match token {
"anonymous-actor" => Some(Self::AnonymousActor),
"expect-actor-mismatch" => Some(Self::ExpectActorMismatch),
"gate-refusal" => Some(Self::GateRefusal),
"strict-op-failure" => Some(Self::StrictOpFailure),
"parse-error" => Some(Self::ParseError),
"verb-refused" => Some(Self::VerbRefused),
"policy-refusal" => Some(Self::PolicyRefusal),
_ => None,
}
}
}
impl fmt::Display for RefusalReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl core::str::FromStr for RefusalReason {
type Err = UnknownVariant;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Self::from_token(value).ok_or_else(|| {
UnknownVariant::new(
"refusal_reason",
value,
&[
"anonymous-actor",
"expect-actor-mismatch",
"gate-refusal",
"strict-op-failure",
"parse-error",
"verb-refused",
"policy-refusal",
],
)
})
}
}
#[cfg(test)]
#[path = "refusal_tests.rs"]
mod tests;