pub struct Codes {
pub content_type: &'static str,
pub credential: &'static str,
pub limit: &'static str,
pub malformed: &'static str,
pub refused: &'static str,
pub unauthorized: &'static str,
pub timeout: &'static str,
pub server_error: &'static str,
pub busy: &'static str,
}
#[macro_export]
macro_rules! refusal_codes {
($family:literal) => {
$crate::refusal::Codes {
content_type: concat!($family, "-content-type"),
credential: concat!($family, "-credential"),
limit: concat!($family, "-limit"),
malformed: concat!($family, "-malformed"),
refused: concat!($family, "-refused"),
unauthorized: concat!($family, "-unauthorized"),
timeout: concat!($family, "-timeout"),
server_error: concat!($family, "-server-error"),
busy: concat!($family, "-busy"),
}
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Bootstrap {
Claimed,
SetupRejected,
SetupExpired,
Quota,
Unknown,
}
impl Bootstrap {
pub fn classify(code: Option<&str>, claim: bool) -> Self {
match code {
Some("bootstrap-storage-already-claimed") => Self::Claimed,
Some("bootstrap-setup-invitation-rejected") if claim => Self::SetupRejected,
Some("bootstrap-setup-invitation-expired") if claim => Self::SetupExpired,
Some("attachment-quota-exceeded") => Self::Quota,
_ => Self::Unknown,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Enrollment {
Stale,
Busy,
Unauthorized,
Timeout,
Refused,
Server,
}
impl Enrollment {
pub fn classify(status: u16, code: Option<&str>) -> Self {
match code {
Some("membership-stale") => Self::Stale,
Some("enrollment-busy") => Self::Busy,
Some("enrollment-unauthorized") => Self::Unauthorized,
Some("enrollment-timeout") => Self::Timeout,
Some(_) if (400..500).contains(&status) => Self::Refused,
_ => Self::Server,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Tail {
Malformed,
BatchKnown,
Stale,
Unauthorized,
PrefixIdentityCollision,
Quota,
Unknown,
}
impl Tail {
pub fn classify(code: Option<&str>) -> Self {
match code {
Some("encrypted-tail-malformed") => Self::Malformed,
Some("encrypted-tail-batch-known") => Self::BatchKnown,
Some("membership-stale") => Self::Stale,
Some("encrypted-tail-unauthorized") => Self::Unauthorized,
Some("encrypted-tail-prefix-identity-collision") => Self::PrefixIdentityCollision,
Some("attachment-quota-exceeded") => Self::Quota,
_ => Self::Unknown,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn endpoint_refusals_preserve_access_and_outcome_uncertainty() {
assert_eq!(
Bootstrap::classify(Some("bootstrap-setup-invitation-expired"), true),
Bootstrap::SetupExpired
);
assert_eq!(
Bootstrap::classify(Some("bootstrap-setup-invitation-expired"), false),
Bootstrap::Unknown
);
assert_eq!(
Enrollment::classify(401, Some("enrollment-credential")),
Enrollment::Refused
);
assert_eq!(
Enrollment::classify(503, Some("enrollment-busy")),
Enrollment::Busy
);
assert_eq!(
Enrollment::classify(403, Some("enrollment-unauthorized")),
Enrollment::Unauthorized
);
assert_eq!(Enrollment::classify(403, None), Enrollment::Server);
assert_eq!(
Tail::classify(Some("encrypted-tail-credential")),
Tail::Unknown
);
assert_eq!(
Tail::classify(Some("encrypted-image-unauthorized")),
Tail::Unknown
);
assert_eq!(
Tail::classify(Some("encrypted-tail-unauthorized")),
Tail::Unauthorized
);
assert_eq!(
Tail::classify(Some("attachment-quota-exceeded")),
Tail::Quota
);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Hosting {
Blocked,
Deleted,
Quota,
Unavailable,
}
pub struct HostingCodes {
pub blocked: &'static str,
pub deleted: &'static str,
pub quota: &'static str,
pub unavailable: &'static str,
}
impl HostingCodes {
pub fn classify(&self, status: u16, code: Option<&str>) -> Option<Hosting> {
match (status, code) {
(403, Some(code)) if code == self.blocked => Some(Hosting::Blocked),
(403, Some(code)) if code == self.deleted => Some(Hosting::Deleted),
(429, Some(code)) if code == self.quota => Some(Hosting::Quota),
(503, Some(code)) if code == self.unavailable => Some(Hosting::Unavailable),
_ => None,
}
}
}
pub const BOOTSTRAP_HOSTING: HostingCodes = HostingCodes {
blocked: "bootstrap-hosting-blocked",
deleted: "bootstrap-hosting-deleted",
quota: "bootstrap-hosting-quota",
unavailable: "bootstrap-hosting-unavailable",
};
pub const ENROLLMENT_HOSTING: HostingCodes = HostingCodes {
blocked: "enrollment-hosting-blocked",
deleted: "enrollment-hosting-deleted",
quota: "enrollment-hosting-quota",
unavailable: "enrollment-hosting-unavailable",
};
pub const TAIL_HOSTING: HostingCodes = HostingCodes {
blocked: "encrypted-tail-hosting-blocked",
deleted: "encrypted-tail-hosting-deleted",
quota: "encrypted-tail-hosting-quota",
unavailable: "encrypted-tail-hosting-unavailable",
};
pub const IMAGE_HOSTING: HostingCodes = HostingCodes {
blocked: "encrypted-image-hosting-blocked",
deleted: "encrypted-image-hosting-deleted",
quota: "encrypted-image-hosting-quota",
unavailable: "encrypted-image-hosting-unavailable",
};
#[cfg(test)]
mod hosting_tests {
use super::*;
#[test]
fn hosting_codes_require_their_endpoint_family_and_status() {
let families = [
&BOOTSTRAP_HOSTING,
&ENROLLMENT_HOSTING,
&TAIL_HOSTING,
&IMAGE_HOSTING,
];
for (i, family) in families.iter().enumerate() {
for (status, code, expected) in [
(403, family.blocked, Hosting::Blocked),
(403, family.deleted, Hosting::Deleted),
(429, family.quota, Hosting::Quota),
(503, family.unavailable, Hosting::Unavailable),
] {
assert_eq!(family.classify(status, Some(code)), Some(expected));
assert_eq!(family.classify(401, Some(code)), None);
for (j, other) in families.iter().enumerate() {
if i != j {
assert_eq!(other.classify(status, Some(code)), None);
}
}
}
assert_eq!(family.classify(403, Some("unknown")), None);
assert_eq!(family.classify(503, None), None);
}
}
}