1use std::collections::HashMap;
5use std::io;
6use std::path::{Path, PathBuf};
7
8use anyhow::Context;
9use serde::{Deserialize, Serialize};
10use toml_edit::{table, value, Document, Item};
11
12const DEFAULT_AUTH_TOML: &str = r#"
13# This is where Wally stores details for authenticating with registries.
14# It can be updated using `wally login` and `wally logout`.
15
16[tokens]
17
18"#;
19
20#[derive(Serialize, Deserialize)]
21pub struct AuthStore {
22 pub tokens: HashMap<String, String>,
23}
24
25impl AuthStore {
26 pub fn load() -> anyhow::Result<Self> {
27 let path = file_path()?;
28 let contents = Self::contents(&path)?;
29
30 let auth = toml::from_str(&contents).with_context(|| {
31 format!(
32 "Malformed Wally auth config file. Try deleting {}",
33 path.display()
34 )
35 })?;
36
37 Ok(auth)
38 }
39
40 pub fn set_token(key: &str, token: Option<&str>) -> anyhow::Result<()> {
41 let path = file_path()?;
42 let contents = Self::contents(&path)?;
43
44 let mut auth: Document = contents.parse().unwrap();
45
46 if !auth.as_table_mut().contains_table("tokens") {
47 auth["tokens"] = table();
48 }
49
50 let tokens = auth.as_table_mut().entry("tokens");
51
52 if let Some(token) = token {
53 tokens[key] = value(token);
54 } else {
55 tokens[key] = Item::None;
56 }
57
58 fs_err::create_dir_all(path.parent().unwrap())?;
59 fs_err::write(&path, auth.to_string())?;
60
61 Ok(())
62 }
63
64 fn contents(path: &Path) -> anyhow::Result<String> {
65 match fs_err::read_to_string(&path) {
66 Ok(contents) => Ok(contents),
67 Err(err) => {
68 if err.kind() == io::ErrorKind::NotFound {
69 Ok(DEFAULT_AUTH_TOML.to_owned())
70 } else {
71 return Err(err.into());
72 }
73 }
74 }
75 }
76}
77
78fn file_path() -> anyhow::Result<PathBuf> {
79 let mut path = dirs::home_dir().context("Failed to find home directory")?;
80 path.push(".wally");
81 path.push("auth.toml");
82 Ok(path)
83}