use platform_core::AppError;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CacheAction {
Put,
Get,
Mget,
Mput,
Delete,
PutIfNotPresent,
ListPush,
ListPop,
ListLen,
}
impl CacheAction {
pub const ALL: [CacheAction; 9] = [
CacheAction::Put,
CacheAction::Get,
CacheAction::Mget,
CacheAction::Mput,
CacheAction::Delete,
CacheAction::PutIfNotPresent,
CacheAction::ListPush,
CacheAction::ListPop,
CacheAction::ListLen,
];
pub fn name(self) -> &'static str {
match self {
CacheAction::Put => "PUT",
CacheAction::Get => "GET",
CacheAction::Mget => "MGET",
CacheAction::Mput => "MPUT",
CacheAction::Delete => "DELETE",
CacheAction::PutIfNotPresent => "PUT_IF_NOT_PRESENT",
CacheAction::ListPush => "LIST_PUSH",
CacheAction::ListPop => "LIST_POP",
CacheAction::ListLen => "LIST_LEN",
}
}
pub fn from_header(action: Option<&str>) -> Result<CacheAction, AppError> {
let text = action.unwrap_or("");
if text.trim().is_empty() {
return Err(AppError::new(
400,
format!("Missing 'action' - one of {}", Self::supported()),
));
}
let wanted = text.trim().to_ascii_uppercase();
Self::ALL
.into_iter()
.find(|candidate| candidate.name() == wanted)
.ok_or_else(|| {
AppError::new(
400,
format!("Unsupported action '{text}' - one of {}", Self::supported()),
)
})
}
fn supported() -> String {
Self::ALL
.iter()
.map(|action| action.name())
.collect::<Vec<_>>()
.join(", ")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolves_case_insensitively() {
assert_eq!(
CacheAction::Get,
CacheAction::from_header(Some("get")).unwrap()
);
assert_eq!(
CacheAction::Put,
CacheAction::from_header(Some(" Put ")).unwrap()
);
assert_eq!(
CacheAction::PutIfNotPresent,
CacheAction::from_header(Some("put_if_not_present")).unwrap()
);
assert_eq!(
CacheAction::ListPush,
CacheAction::from_header(Some("LIST_PUSH")).unwrap()
);
}
#[test]
fn missing_action_names_the_supported_set() {
for absent in [None, Some(""), Some(" ")] {
let error = CacheAction::from_header(absent).unwrap_err();
assert_eq!(400, error.status());
assert!(error
.message()
.starts_with("Missing 'action' - one of PUT, GET, MGET"));
assert!(error.message().ends_with("LIST_LEN"));
}
}
#[test]
fn unsupported_action_is_named_in_the_error() {
let error = CacheAction::from_header(Some("INCR")).unwrap_err();
assert_eq!(400, error.status());
assert!(error
.message()
.starts_with("Unsupported action 'INCR' - one of "));
assert!(error.message().contains("PUT_IF_NOT_PRESENT"));
}
}