use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StoragePersistence {
Granted,
Refused,
Unknown(String),
}
impl StoragePersistence {
pub fn is_durable(&self) -> bool {
matches!(self, Self::Granted)
}
pub fn token(&self) -> &'static str {
match self {
Self::Granted => "granted",
Self::Refused => "refused",
Self::Unknown(_) => "unknown",
}
}
}
impl fmt::Display for StoragePersistence {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Granted => write!(formatter, "persistent"),
Self::Refused => write!(formatter, "not persistent, may be evicted"),
Self::Unknown(_) => write!(formatter, "persistence unknown"),
}
}
}
pub fn decide(
persisted: Result<bool, String>,
requested: impl FnOnce() -> Result<bool, String>,
) -> StoragePersistence {
match persisted {
Ok(true) => StoragePersistence::Granted,
Ok(false) => match requested() {
Ok(true) => StoragePersistence::Granted,
Ok(false) => StoragePersistence::Refused,
Err(reason) => StoragePersistence::Unknown(reason),
},
Err(reason) => StoragePersistence::Unknown(reason),
}
}
pub fn status_line(store: &str, persistence: &StoragePersistence) -> String {
format!("{store} · {persistence}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_a_granted_request_counts_as_durable() {
assert!(StoragePersistence::Granted.is_durable());
assert!(!StoragePersistence::Refused.is_durable());
assert!(
!StoragePersistence::Unknown("no storage manager".into()).is_durable(),
"an unanswered question is not a yes"
);
}
#[test]
fn an_already_persistent_profile_is_not_asked_again() {
let mut asked = false;
let state = decide(Ok(true), || {
asked = true;
Ok(false)
});
assert_eq!(state, StoragePersistence::Granted);
assert!(
!asked,
"requesting what is already granted is a wasted prompt"
);
}
#[test]
fn a_refusal_and_an_unanswerable_question_stay_distinct() {
assert_eq!(decide(Ok(false), || Ok(true)), StoragePersistence::Granted);
assert_eq!(decide(Ok(false), || Ok(false)), StoragePersistence::Refused);
let no_api = decide(Err("no storage manager".into()), || Ok(true));
let ask_failed = decide(Ok(false), || Err("persist() rejected".into()));
assert_eq!(
no_api,
StoragePersistence::Unknown("no storage manager".into())
);
assert_eq!(
ask_failed,
StoragePersistence::Unknown("persist() rejected".into())
);
assert_ne!(no_api, StoragePersistence::Refused);
assert_ne!(ask_failed, StoragePersistence::Refused);
}
#[test]
fn the_status_line_never_reports_a_store_without_its_durability() {
assert_eq!(
status_line("IndexedDB reopened", &StoragePersistence::Granted),
"IndexedDB reopened · persistent"
);
assert_eq!(
status_line("IndexedDB reopened", &StoragePersistence::Refused),
"IndexedDB reopened · not persistent, may be evicted"
);
assert_eq!(
status_line("IndexedDB seeded", &StoragePersistence::Unknown("x".into())),
"IndexedDB seeded · persistence unknown"
);
}
#[test]
fn tokens_are_stable_for_automation() {
assert_eq!(StoragePersistence::Granted.token(), "granted");
assert_eq!(StoragePersistence::Refused.token(), "refused");
assert_eq!(
StoragePersistence::Unknown(String::new()).token(),
"unknown"
);
}
}