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}