use alloc::string::{String, ToString};
use pamoja_core::Error;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Refusal {
Malformed,
UnsupportedVersion,
Signature,
Digest,
Size,
WrongDevice,
Rollback,
Expired,
NoClock,
SlotTooSmall,
NoSuchSlot,
WrongState,
NothingToRevert,
}
impl core::fmt::Display for Refusal {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.reason())
}
}
impl Refusal {
pub fn reason(self) -> &'static str {
match self {
Self::Malformed => "the manifest is malformed",
Self::UnsupportedVersion => "the manifest structure version is not supported",
Self::Signature => "the manifest signature is not from the trusted key",
Self::Digest => "the image does not match the manifest digest",
Self::Size => "the image is not the size the manifest declares",
Self::WrongDevice => "the manifest is for a different vendor or device class",
Self::Rollback => "the sequence number would roll the device back",
Self::Expired => "the manifest has expired",
Self::NoClock => "the manifest expires and this device has no clock",
Self::SlotTooSmall => "the image does not fit the target slot",
Self::NoSuchSlot => "no such slot on this device",
Self::WrongState => "the slot is not in a state that allows this",
Self::NothingToRevert => "there is no confirmed image to revert to",
}
}
}
impl From<Refusal> for Error {
fn from(value: Refusal) -> Self {
let message: String = value.reason().to_string();
match value {
Refusal::Signature
| Refusal::Digest
| Refusal::WrongDevice
| Refusal::Rollback
| Refusal::Expired => Error::Auth(message),
Refusal::Malformed | Refusal::UnsupportedVersion => Error::Codec(message),
_ => Error::Io(message),
}
}
}
pub type Result<T> = core::result::Result<T, Refusal>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn authenticity_failures_map_to_auth_errors() {
for refusal in [
Refusal::Signature,
Refusal::Digest,
Refusal::WrongDevice,
Refusal::Rollback,
] {
assert!(matches!(Error::from(refusal), Error::Auth(_)));
}
}
#[test]
fn parse_failures_map_to_codec_errors() {
assert!(matches!(Error::from(Refusal::Malformed), Error::Codec(_)));
assert!(matches!(
Error::from(Refusal::UnsupportedVersion),
Error::Codec(_)
));
}
#[test]
fn every_refusal_names_its_rule() {
assert!(!Refusal::Rollback.reason().is_empty());
assert!(Refusal::Rollback.reason().contains("roll"));
}
}