novel_api/common/utils/
keyring.rs

1use keyring::Entry;
2use zeroize::Zeroize;
3
4use crate::Error;
5
6/// Access the Keyring of the platform
7#[must_use]
8pub struct Keyring {
9    entry: Entry,
10}
11
12impl Keyring {
13    /// Create a Keyring
14    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        let service = format!("novel-rs-{}", app_name.as_ref());
20        let entry = Entry::new(&service, username.as_ref())?;
21
22        Ok(Self { entry })
23    }
24
25    /// Get password
26    pub fn get_password(&self) -> Result<String, Error> {
27        Ok(self.entry.get_password()?)
28    }
29
30    /// Set password
31    pub fn set_password(&self, mut password: String) -> Result<(), Error> {
32        self.entry.set_password(&password)?;
33        password.zeroize();
34        Ok(())
35    }
36
37    /// Delete password
38    pub fn delete_password(&self) -> Result<(), Error> {
39        Ok(self.entry.delete_credential()?)
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use pretty_assertions::assert_eq;
46
47    use super::*;
48
49    #[tokio::test]
50    async fn keyring() -> Result<(), Error> {
51        let keyring = Keyring::new("test-app", "user-name")?;
52        let password = "test-password".to_string();
53
54        keyring.set_password(password.clone())?;
55        assert_eq!(keyring.get_password()?, password);
56
57        keyring.delete_password()?;
58
59        Ok(())
60    }
61}