| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
- use keyring::Entry;
- /// 令牌存取:Windows → Credential Manager,macOS → Keychain
- pub struct TokenStore {
- service: String,
- account: String,
- }
- impl TokenStore {
- pub fn new() -> Self {
- Self::with_names("xafpet", "jwt")
- }
- pub fn with_names(service: &str, account: &str) -> Self {
- Self { service: service.into(), account: account.into() }
- }
- pub fn save(&self, token: &str) -> Result<(), String> {
- Entry::new(&self.service, &self.account)
- .map_err(|e| e.to_string())?
- .set_password(token)
- .map_err(|e| e.to_string())
- }
- pub fn load(&self) -> Result<Option<String>, String> {
- let entry = Entry::new(&self.service, &self.account).map_err(|e| e.to_string())?;
- match entry.get_password() {
- Ok(p) => Ok(Some(p)),
- Err(keyring::Error::NoEntry) => Ok(None),
- Err(e) => Err(e.to_string()),
- }
- }
- pub fn clear(&self) -> Result<(), String> {
- let entry = Entry::new(&self.service, &self.account).map_err(|e| e.to_string())?;
- match entry.delete_credential() {
- Ok(()) => Ok(()),
- Err(keyring::Error::NoEntry) => Ok(()),
- Err(e) => Err(e.to_string()),
- }
- }
- }
- impl Default for TokenStore {
- fn default() -> Self {
- Self::new()
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[cfg(not(target_os = "macos"))]
- #[test]
- fn roundtrip_save_load_clear() {
- let store = TokenStore::with_names("xafpet-test", "jwt");
- let _ = store.clear();
- assert_eq!(store.load().unwrap(), None);
- store.save("tok-123").unwrap();
- assert_eq!(store.load().unwrap(), Some("tok-123".into()));
- store.save("tok-456").unwrap();
- assert_eq!(store.load().unwrap(), Some("tok-456".into()));
- store.clear().unwrap();
- assert_eq!(store.load().unwrap(), None);
- }
- }
|