azul_layout/managers/
keyring.rs1use alloc::vec::Vec;
21
22pub use azul_core::keyring::{KeyringRequest, KeyringResult};
26
27#[derive(Debug, Clone, PartialEq, Default)]
30pub struct KeyringManager {
31 pub last_result: Option<KeyringResult>,
34}
35
36impl KeyringManager {
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn last_result(&self) -> Option<&KeyringResult> {
43 self.last_result.as_ref()
44 }
45
46 pub fn set_last_result(&mut self, result: KeyringResult) -> bool {
50 let changed = self.last_result.as_ref() != Some(&result);
51 self.last_result = Some(result);
52 changed
53 }
54}
55
56static PENDING_REQUESTS: std::sync::Mutex<Vec<KeyringRequest>> =
59 std::sync::Mutex::new(Vec::new());
60
61pub fn push_keyring_request(request: KeyringRequest) {
64 let mut q = PENDING_REQUESTS.lock().unwrap_or_else(|e| e.into_inner());
65 q.push(request);
66}
67
68pub fn drain_keyring_requests() -> Vec<KeyringRequest> {
71 let mut q = PENDING_REQUESTS.lock().unwrap_or_else(|e| e.into_inner());
72 core::mem::take(&mut *q)
73}
74
75static PENDING_RESULTS: std::sync::Mutex<Vec<KeyringResult>> =
78 std::sync::Mutex::new(Vec::new());
79
80pub fn push_keyring_result(result: KeyringResult) {
84 let mut q = PENDING_RESULTS.lock().unwrap_or_else(|e| e.into_inner());
85 q.push(result);
86}
87
88pub fn drain_keyring_results() -> Vec<KeyringResult> {
91 let mut q = PENDING_RESULTS.lock().unwrap_or_else(|e| e.into_inner());
92 core::mem::take(&mut *q)
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use azul_css::AzString;
99
100 #[test]
101 fn manager_defaults_to_no_result() {
102 let mgr = KeyringManager::new();
103 assert_eq!(mgr.last_result(), None);
104 }
105
106 #[test]
107 fn set_last_result_returns_change_flag() {
108 let mut mgr = KeyringManager::new();
109 assert!(mgr.set_last_result(KeyringResult::Stored));
110 assert_eq!(mgr.last_result(), Some(&KeyringResult::Stored));
111 assert!(!mgr.set_last_result(KeyringResult::Stored));
113 assert!(mgr.set_last_result(KeyringResult::Deleted));
115 }
116
117 #[test]
118 fn result_helpers() {
119 let secret = KeyringResult::Retrieved(AzString::from_const_str("hunter2"));
120 assert_eq!(secret.secret().map(|s| s.as_str()), Some("hunter2"));
121 assert!(secret.is_ok());
122 assert!(KeyringResult::Stored.is_ok());
123 assert!(KeyringResult::Deleted.is_ok());
124 for r in [
125 KeyringResult::NotFound,
126 KeyringResult::Denied,
127 KeyringResult::Unavailable,
128 KeyringResult::Error,
129 ] {
130 assert!(!r.is_ok(), "{:?} must not be ok", r);
131 assert_eq!(r.secret(), None);
132 }
133 }
134
135 #[test]
136 fn requests_round_trip_through_channel() {
137 let _ = drain_keyring_requests();
138
139 push_keyring_request(KeyringRequest::Store {
140 key: AzString::from_const_str("token"),
141 secret: AzString::from_const_str("abc"),
142 require_biometry: true,
143 });
144 push_keyring_request(KeyringRequest::Get {
145 key: AzString::from_const_str("token"),
146 });
147 let drained = drain_keyring_requests();
148 assert_eq!(drained.len(), 2, "both queued requests drain in order");
149 assert!(matches!(drained[0], KeyringRequest::Store { .. }));
150 assert!(matches!(drained[1], KeyringRequest::Get { .. }));
151 assert!(drain_keyring_requests().is_empty());
152 }
153
154 #[test]
155 fn results_round_trip_through_manager() {
156 let _ = drain_keyring_results();
157
158 push_keyring_result(KeyringResult::NotFound);
159 push_keyring_result(KeyringResult::Retrieved(AzString::from_const_str("s"))); let drained = drain_keyring_results();
161 assert_eq!(drained.len(), 2);
162
163 let mut mgr = KeyringManager::new();
164 for r in drained {
165 mgr.set_last_result(r);
166 }
167 assert_eq!(
168 mgr.last_result().and_then(|r| r.secret()).map(|s| s.as_str()),
169 Some("s"),
170 "the last applied result wins"
171 );
172 assert!(drain_keyring_results().is_empty());
173 }
174}