tmc_langs/config/
credentials.rs

1//! Contains the Credentials struct for authenticating with tmc-server.
2
3use crate::{LangsError, tmc::Token};
4use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6use tmc_langs_util::{
7    deserialize,
8    file_util::{self, Lock, LockOptions},
9};
10
11/// Credentials for authenticating with tmc-server.
12#[derive(Debug, Serialize, Deserialize)]
13pub struct Credentials {
14    path: PathBuf,
15    token: Token,
16}
17
18impl Credentials {
19    // path to the credentials file
20    fn get_credentials_path(client_name: &str) -> Result<PathBuf, LangsError> {
21        super::get_tmc_dir(client_name).map(|dir| dir.join("credentials.json"))
22    }
23
24    /// ### Returns
25    /// - Ok(Some) if a credentials file exists and can be deserialized,
26    /// - Ok(None) if no credentials file exists, and
27    /// - Err if a credentials file exists but cannot be deserialized.
28    ///
29    /// On Err, the file is deleted.
30    pub fn load(client_name: &str) -> Result<Option<Self>, LangsError> {
31        let credentials_path = Self::get_credentials_path(client_name)?;
32        if !credentials_path.exists() {
33            return Ok(None);
34        }
35        log::debug!("Loading credentials from {}", credentials_path.display());
36
37        let mut credentials_lock = Lock::file(&credentials_path, LockOptions::Read)?;
38        let credentials_guard = credentials_lock.lock()?;
39        match deserialize::json_from_reader(credentials_guard.get_file()) {
40            Ok(token) => Ok(Some(Credentials {
41                path: credentials_path,
42                token,
43            })),
44            Err(e) => {
45                log::error!("Failed to deserialize credentials.json due to \"{e}\", deleting");
46                file_util::remove_file(&credentials_path)?;
47                Err(LangsError::DeserializeCredentials(credentials_path, e))
48            }
49        }
50    }
51
52    pub fn save(client_name: &str, token: Token) -> Result<(), LangsError> {
53        let credentials_path = Self::get_credentials_path(client_name)?;
54
55        if let Some(p) = credentials_path.parent() {
56            file_util::create_dir_all(p)?;
57        }
58        let mut credentials_lock = Lock::file(&credentials_path, LockOptions::WriteTruncate)?;
59        let mut credentials_guard = credentials_lock.lock()?;
60        // write token
61        if let Err(e) = serde_json::to_writer(credentials_guard.get_file_mut(), &token) {
62            // failed to write token, removing credentials file
63            file_util::remove_file(&credentials_path)?;
64            return Err(LangsError::Json(e));
65        }
66        Ok(())
67    }
68
69    pub fn remove(self) -> Result<(), LangsError> {
70        file_util::remove_file_locked(self.path)?;
71        Ok(())
72    }
73
74    pub fn token(&self) -> Token {
75        self.token.clone()
76    }
77}