qlty_cloud/
token.rs

1use anyhow::{bail, Context, Result};
2use dialoguer::{theme::ColorfulTheme, Confirm, Input};
3use keyring::Entry;
4
5use crate::Client;
6
7const KEYRING_SERVICE: &str = "qlty-cli";
8
9pub struct Token {
10    pub user: String,
11}
12
13impl Default for Token {
14    fn default() -> Self {
15        Self::new("default")
16    }
17}
18
19impl Token {
20    pub fn new(user: &str) -> Self {
21        Self {
22            user: user.to_owned(),
23        }
24    }
25
26    pub fn get_with_interactive_prompt(&self) -> Result<String> {
27        match self.get() {
28            Ok(token) => Ok(token),
29            Err(_) => {
30                if Confirm::with_theme(&ColorfulTheme::default())
31                    .with_prompt("This action requires a CLI access token. Do you want to log in?")
32                    .default(true)
33                    .show_default(true)
34                    .interact()?
35                {
36                    webbrowser::open("https://qlty.sh/user/settings/cli")?;
37
38                    let access_token: String = Input::new()
39                        .with_prompt("CLI access token")
40                        .interact_text()
41                        .unwrap();
42
43                    let client = Client {
44                        base_url: "https://api.qlty.sh".to_string(),
45                        token: Some(access_token.clone()),
46                    };
47
48                    match client.get("/user").call() {
49                        Ok(_) => {
50                            self.set(&access_token)?;
51                            Ok(access_token)
52                        }
53                        Err(e) => {
54                            bail!("Failed to authenticate: {}", e);
55                        }
56                    }
57                } else {
58                    bail!("Please run `qlty auth login` to provide an access token to continue.")
59                }
60            }
61        }
62    }
63
64    pub fn get(&self) -> Result<String> {
65        self.keyring_entry()?.get_password().with_context(|| {
66            format!(
67                "Failed to get access token for service '{}' and user '{}'",
68                KEYRING_SERVICE, self.user
69            )
70        })
71    }
72
73    pub fn set(&self, token: &str) -> Result<()> {
74        self.keyring_entry()?.set_password(token).with_context(|| {
75            format!(
76                "Failed to set access token for service '{}' and user '{}'",
77                KEYRING_SERVICE, self.user
78            )
79        })
80    }
81
82    pub fn delete(&self) -> Result<()> {
83        self.keyring_entry()?.delete_credential().with_context(|| {
84            format!(
85                "Failed to delete access token for service '{}' and user '{}'",
86                KEYRING_SERVICE, self.user
87            )
88        })
89    }
90
91    fn keyring_entry(&self) -> Result<Entry> {
92        Entry::new(KEYRING_SERVICE, &self.user).with_context(|| {
93            format!(
94                "Failed to create keyring entry for service '{}' and user '{}'",
95                KEYRING_SERVICE, self.user
96            )
97        })
98    }
99}