use azul_css::AzString;
#[repr(C, u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyringRequest {
Store {
key: AzString,
secret: AzString,
require_biometry: bool,
},
Get { key: AzString },
Delete { key: AzString },
}
#[allow(variant_size_differences)] #[repr(C, u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyringResult {
Stored,
Retrieved(AzString),
Deleted,
NotFound,
Denied,
Unavailable,
Error,
}
impl KeyringResult {
#[must_use] pub const fn secret(&self) -> Option<&AzString> {
match self {
Self::Retrieved(s) => Some(s),
_ => None,
}
}
#[must_use] pub const fn is_ok(&self) -> bool {
matches!(
self,
Self::Stored | Self::Retrieved(_) | Self::Deleted
)
}
}
impl_option!(
KeyringResult,
OptionKeyringResult,
copy = false,
[Debug, Clone, PartialEq, Eq]
);
#[cfg(test)]
mod autotest_generated {
use super::*;
use alloc::string::String;
const NASTY_BYTES: &str = "pw\0with\u{1}nul\u{7f}\r\n\t";
const NASTY_UNICODE: &str = "🔑🗝 ключ 鍵 مفتاح e\u{301}\u{202e}terces\u{202c}\u{fffd}\u{10ffff}";
fn all_variants() -> [KeyringResult; 7] {
[
KeyringResult::Stored,
KeyringResult::Retrieved(AzString::from("s3cr3t")),
KeyringResult::Deleted,
KeyringResult::NotFound,
KeyringResult::Denied,
KeyringResult::Unavailable,
KeyringResult::Error,
]
}
fn expected(r: &KeyringResult) -> (bool, bool) {
match r {
KeyringResult::Stored => (true, false),
KeyringResult::Retrieved(_) => (true, true),
KeyringResult::Deleted => (true, false),
KeyringResult::NotFound => (false, false),
KeyringResult::Denied => (false, false),
KeyringResult::Unavailable => (false, false),
KeyringResult::Error => (false, false),
}
}
#[test]
fn secret_returns_the_exact_payload_of_retrieved() {
let r = KeyringResult::Retrieved(AzString::from("hunter2"));
assert_eq!(r.secret().expect("Retrieved must yield a secret").as_str(), "hunter2");
}
#[test]
fn secret_is_none_for_every_non_retrieved_variant() {
for r in &all_variants() {
let has_secret = expected(r).1;
assert_eq!(
r.secret().is_some(),
has_secret,
"secret() disagrees with the documented contract for {r:?}"
);
}
}
#[test]
fn secret_of_an_empty_payload_is_some_empty_not_none() {
let r = KeyringResult::Retrieved(AzString::from(""));
assert_eq!(r.secret().expect("empty payload is still a payload").as_str(), "");
assert!(r.is_ok());
let d = KeyringResult::Retrieved(AzString::default());
assert_eq!(d.secret().expect("default payload is still a payload").as_str(), "");
assert!(d.is_ok());
}
#[test]
fn secret_preserves_interior_nul_and_control_bytes() {
let r = KeyringResult::Retrieved(AzString::from(NASTY_BYTES));
let s = r.secret().expect("Retrieved must yield a secret");
assert_eq!(s.as_str(), NASTY_BYTES);
assert_eq!(s.as_str().as_bytes(), NASTY_BYTES.as_bytes());
assert_eq!(s.as_str().len(), NASTY_BYTES.len());
}
#[test]
fn secret_preserves_multibyte_and_max_scalar_boundaries() {
let r = KeyringResult::Retrieved(AzString::from(NASTY_UNICODE));
let s = r.secret().expect("Retrieved must yield a secret");
assert_eq!(s.as_str(), NASTY_UNICODE);
assert_eq!(s.as_str().as_bytes(), NASTY_UNICODE.as_bytes());
assert_eq!(s.as_str().chars().count(), NASTY_UNICODE.chars().count());
assert!(s.as_str().ends_with('\u{10ffff}'));
}
#[test]
fn secret_handles_a_megabyte_payload() {
let big = "k".repeat(1 << 20);
let r = KeyringResult::Retrieved(AzString::from(big.clone()));
let s = r.secret().expect("Retrieved must yield a secret");
assert_eq!(s.as_str().len(), 1 << 20);
assert_eq!(s.as_str(), big.as_str());
assert!(r.is_ok());
}
#[test]
fn secret_borrows_the_payload_in_place_and_is_repeatable() {
let r = KeyringResult::Retrieved(AzString::from("borrow-me"));
let inner: &AzString = match &r {
KeyringResult::Retrieved(s) => s,
other => unreachable!("constructed Retrieved, got {other:?}"),
};
let first = r.secret().expect("Retrieved must yield a secret");
assert!(
core::ptr::eq(first, inner),
"secret() must borrow the payload, not hand back a copy"
);
let second = r.secret().expect("secret() must be repeatable");
assert!(core::ptr::eq(first, second));
assert_eq!(first.as_str().as_ptr(), second.as_str().as_ptr());
}
#[test]
fn is_ok_truth_table_is_exhaustive() {
for r in &all_variants() {
let want = expected(r).0;
assert_eq!(r.is_ok(), want, "is_ok() wrong for {r:?}");
}
assert!(KeyringResult::Stored.is_ok());
assert!(KeyringResult::Retrieved(AzString::from("x")).is_ok());
assert!(KeyringResult::Deleted.is_ok());
assert!(!KeyringResult::NotFound.is_ok());
assert!(!KeyringResult::Denied.is_ok());
assert!(!KeyringResult::Unavailable.is_ok());
assert!(!KeyringResult::Error.is_ok());
}
#[test]
fn is_ok_is_independent_of_the_retrieved_payload() {
let payloads = [
String::new(),
String::from(NASTY_BYTES),
String::from(NASTY_UNICODE),
"a".repeat(100_000),
];
for p in payloads {
assert!(
KeyringResult::Retrieved(AzString::from(p)).is_ok(),
"Retrieved is a success outcome whatever it carries"
);
}
}
#[test]
fn is_ok_and_secret_never_contradict_each_other() {
for r in &all_variants() {
if r.secret().is_some() {
assert!(r.is_ok(), "{r:?} yielded a secret but reports failure");
}
if !r.is_ok() {
assert!(r.secret().is_none(), "failure {r:?} yielded a secret");
}
}
}
#[test]
fn accessors_are_pure() {
for r in &all_variants() {
let before = r.clone();
for _ in 0..8 {
assert_eq!(r.is_ok(), before.is_ok());
assert_eq!(r.secret().is_some(), before.secret().is_some());
}
assert_eq!(*r, before, "accessors must not mutate the receiver");
}
}
#[test]
fn dropping_a_clone_leaves_the_original_payload_readable() {
let payload = "🔐 secret-that-must-survive";
let original = KeyringResult::Retrieved(AzString::from(payload));
for _ in 0..1000 {
let c = original.clone();
assert_eq!(c.secret().expect("clone keeps the payload").as_str(), payload);
drop(c);
}
assert_eq!(
original.secret().expect("original survives 1000 clone/drop cycles").as_str(),
payload
);
assert!(original.is_ok());
}
#[test]
fn dropping_the_original_leaves_the_clone_payload_readable() {
let payload = NASTY_UNICODE;
let original = KeyringResult::Retrieved(AzString::from(payload));
let clone = original.clone();
drop(original);
assert_eq!(clone.secret().expect("clone owns its payload").as_str(), payload);
}
#[test]
fn eq_is_sensitive_to_both_variant_and_payload() {
assert_eq!(
KeyringResult::Retrieved(AzString::from("a")),
KeyringResult::Retrieved(AzString::from("a"))
);
assert_ne!(
KeyringResult::Retrieved(AzString::from("a")),
KeyringResult::Retrieved(AzString::from("b"))
);
assert_ne!(KeyringResult::Retrieved(AzString::from("")), KeyringResult::Stored);
assert_ne!(KeyringResult::NotFound, KeyringResult::Denied);
assert_ne!(KeyringResult::Unavailable, KeyringResult::Error);
assert_eq!(KeyringResult::Deleted, KeyringResult::Deleted);
}
#[test]
fn option_wrapper_round_trips_none_and_every_variant() {
let none: OptionKeyringResult = Option::<KeyringResult>::None.into();
assert!(none.is_none());
assert_eq!(Option::<KeyringResult>::from(none), None);
assert!(OptionKeyringResult::default().is_none());
for r in all_variants() {
let wrapped: OptionKeyringResult = Some(r.clone()).into();
let back: Option<KeyringResult> = wrapped.into();
assert_eq!(back, Some(r), "encode/decode through OptionKeyringResult lost data");
}
}
#[test]
fn option_wrapper_accessors_agree_with_each_other() {
let payload = "round-trip-me";
let some = OptionKeyringResult::Some(KeyringResult::Retrieved(AzString::from(payload)));
assert!(some.is_some());
assert!(!some.is_none());
assert_eq!(
some.as_option().and_then(KeyringResult::secret).map(AzString::as_str),
Some(payload)
);
assert_eq!(some.as_ref(), some.as_option());
let none = OptionKeyringResult::None;
assert!(none.is_none());
assert!(!none.is_some());
assert!(none.as_option().is_none());
}
#[test]
fn option_wrapper_into_option_clones_and_leaves_the_wrapper_intact() {
let payload = NASTY_BYTES;
let wrapped = OptionKeyringResult::Some(KeyringResult::Retrieved(AzString::from(payload)));
let first = wrapped.into_option().expect("Some stays Some");
let second = wrapped.into_option().expect("into_option must not consume the wrapper");
assert_eq!(first, second);
assert_eq!(first.secret().expect("payload survives").as_str(), payload);
drop(first);
assert_eq!(second.secret().expect("payload survives its sibling").as_str(), payload);
drop(second);
assert_eq!(
wrapped.as_option().and_then(KeyringResult::secret).map(AzString::as_str),
Some(payload),
"wrapper must still own its payload after both clones died"
);
}
#[test]
fn option_wrapper_replace_returns_the_previous_value() {
let mut o = OptionKeyringResult::None;
assert_eq!(o.replace(KeyringResult::Stored), OptionKeyringResult::None);
let prev = o.replace(KeyringResult::Retrieved(AzString::from("new")));
assert_eq!(prev, OptionKeyringResult::Some(KeyringResult::Stored));
assert_eq!(
o.as_option().and_then(KeyringResult::secret).map(AzString::as_str),
Some("new")
);
}
#[test]
fn option_wrapper_as_mut_can_overwrite_a_retrieved_payload() {
let mut o = OptionKeyringResult::Some(KeyringResult::Retrieved(AzString::from("old")));
if let Some(slot) = o.as_mut() {
*slot = KeyringResult::Deleted; }
assert_eq!(o.as_option(), Some(&KeyringResult::Deleted));
assert!(o.as_option().expect("still Some").is_ok());
assert!(o.as_option().expect("still Some").secret().is_none());
}
#[test]
fn request_equality_is_sensitive_to_key_secret_and_biometry_flag() {
let store = KeyringRequest::Store {
key: AzString::from("k"),
secret: AzString::from("s"),
require_biometry: false,
};
assert_eq!(store, store.clone());
assert_ne!(
store,
KeyringRequest::Store {
key: AzString::from("k"),
secret: AzString::from("s"),
require_biometry: true,
},
"require_biometry changes the stored access control — it must not be ignored"
);
assert_ne!(
store,
KeyringRequest::Store {
key: AzString::from("k"),
secret: AzString::from("other"),
require_biometry: false,
}
);
assert_ne!(
KeyringRequest::Get { key: AzString::from("k") },
KeyringRequest::Delete { key: AzString::from("k") },
"a read and a delete of the same key are different operations"
);
assert_ne!(
KeyringRequest::Get { key: AzString::from("a") },
KeyringRequest::Get { key: AzString::from("b") }
);
}
#[test]
fn request_payloads_survive_clone_and_drop() {
let key = "🔑".repeat(10_000);
let req = KeyringRequest::Store {
key: AzString::from(key.clone()),
secret: AzString::from(NASTY_BYTES),
require_biometry: true,
};
let clone = req.clone();
drop(req);
match &clone {
KeyringRequest::Store { key: k, secret: s, require_biometry } => {
assert_eq!(k.as_str(), key.as_str());
assert_eq!(s.as_str(), NASTY_BYTES);
assert!(*require_biometry);
}
other => unreachable!("constructed Store, got {other:?}"),
}
}
#[test]
fn request_accepts_empty_keys_without_panicking() {
let empty = KeyringRequest::Get { key: AzString::from("") };
assert_eq!(empty, KeyringRequest::Get { key: AzString::default() });
assert_ne!(empty, KeyringRequest::Get { key: AzString::from("k") });
}
}