novel_api/common/utils/
keyring.rs1use keyring_core::Entry;
2use zeroize::Zeroize;
3
4use crate::Error;
5
6#[must_use]
8pub struct Keyring {
9 entry: Entry,
10}
11
12impl Keyring {
13 pub fn new<T, E>(app_name: T, username: E) -> Result<Self, Error>
15 where
16 T: AsRef<str>,
17 E: AsRef<str>,
18 {
19 #[cfg(target_os = "macos")]
20 {
21 use apple_native_keyring_store::keychain::Store;
22 keyring_core::set_default_store(Store::new()?);
23 }
24 #[cfg(target_os = "windows")]
25 {
26 use windows_native_keyring_store::Store;
27 keyring_core::set_default_store(Store::new()?);
28 }
29 #[cfg(target_os = "linux")]
30 {
31 use linux_keyutils_keyring_store::Store;
32 keyring_core::set_default_store(Store::new()?);
33 }
34
35 let service = format!("novel-rs-{}", app_name.as_ref());
36 let entry = Entry::new(&service, username.as_ref())?;
37
38 Ok(Self { entry })
39 }
40
41 pub fn get_password(&self) -> Result<String, Error> {
43 Ok(self.entry.get_password()?)
44 }
45
46 pub fn set_password(&self, mut password: String) -> Result<(), Error> {
48 self.entry.set_password(&password)?;
49 password.zeroize();
50 Ok(())
51 }
52
53 pub fn delete_password(&self) -> Result<(), Error> {
55 Ok(self.entry.delete_credential()?)
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use pretty_assertions::assert_eq;
62
63 use super::*;
64
65 #[tokio::test]
66 async fn keyring() -> Result<(), Error> {
67 let keyring = Keyring::new("test-app", "user-name")?;
68 let password = "test-password".to_string();
69
70 keyring.set_password(password.clone())?;
71 assert_eq!(keyring.get_password()?, password);
72
73 keyring.delete_password()?;
74
75 Ok(())
76 }
77}