1use crate::error::Error;
3use crate::secrets::{Mask, Secret};
4use serde::{Deserialize, Serialize};
5use std::env;
6use std::fs;
7use std::path::Path;
8use std::{collections::HashMap, fmt::Display};
9use url::Url;
10
11pub enum Defaults {
13 Config,
14 Profile,
15 Username,
16 Host,
17 Port,
18 Pwd,
19 Token,
20}
21
22impl Display for Defaults {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 let envar = match self {
25 Defaults::Config => "DR_CONFIG",
26 Defaults::Profile => "DR_PROFILE",
27 Defaults::Username => "DR_USER",
28 Defaults::Host => "DR_HOST",
29 Defaults::Port => "DR_PORT",
30 Defaults::Pwd => "DR_PWD",
31 Defaults::Token => "DR_TOKEN",
32 };
33 write!(f, "{}", envar)
34 }
35}
36
37impl From<Defaults> for String {
38 fn from(value: Defaults) -> Self {
39 format!("{}", value)
40 }
41}
42
43impl From<&Defaults> for String {
44 fn from(value: &Defaults) -> Self {
45 format!("{}", value)
46 }
47}
48
49pub fn load(path: &Path) -> Result<HashMap<String, Profile>, Error> {
51 fs::read_to_string(path).map_err(Error::from).and_then(|s| {
52 ron::from_str(&s)
53 .map_err(|e| e.to_string())
54 .map_err(Error::from)
55 })
56}
57
58pub fn default() -> Result<HashMap<String, Profile>, Error> {
60 let def = env::var(Defaults::Config.to_string()).map_err(Error::from)?;
61 let path = Path::new(&def);
62 load(path)
63}
64
65#[derive(Debug, Deserialize, Serialize, Clone)]
67pub struct Profile {
68 pub username: String,
69 pub secret: Secret<String>,
70 pub host: Url,
71}
72
73impl Profile {
74 pub fn try_default() -> Result<Self, Error> {
76 let dr_config = env::var(Defaults::Config.to_string())?;
77 let path = Path::new(&dr_config);
78 let name = env::var(Defaults::Profile.to_string())?;
79 Profile::load_with(path, &name)
80 }
81
82 pub fn load_with(path: &Path, name: &str) -> Result<Self, Error> {
84 let conf = load(path)?;
85 let profile = conf
86 .get(name)
87 .ok_or(format!("Profile {} is not in {:?}", name, path))?;
88 Ok(profile.to_owned())
89 }
90
91 pub fn new(username: &str, secret: Secret<String>, host: Url) -> Self {
93 Profile {
94 username: String::from(username),
95 secret,
96 host,
97 }
98 }
99
100 pub fn from_envars(
102 username: Option<&str>,
103 token: Option<Secret<String>>,
104 pwd: Option<Secret<String>>,
105 host: Option<Url>,
106 port: Option<u16>,
107 ) -> Result<Self, Error> {
108 let username = match username {
109 Some(val) => String::from(val),
110 None => env::var(Defaults::Username.to_string())?,
111 };
112 let secret = match (token, pwd) {
113 (Some(inner), None) => match inner {
114 Secret::Token(mask) => Secret::Token(mask),
115 Secret::Pwd(mask) => Secret::Token(mask),
116 },
117 (None, Some(inner)) => match inner {
118 Secret::Token(mask) => Secret::Pwd(mask),
119 Secret::Pwd(mask) => Secret::Pwd(mask),
120 },
121 (Some(t), Some(_)) => match t {
122 Secret::Token(mask) => Secret::Token(mask),
123 Secret::Pwd(mask) => Secret::Token(mask),
124 },
125 (None, None) => match env::var(Defaults::Token.to_string()) {
126 Ok(token_val) => Secret::Token(Mask::from(token_val)),
127 Err(_) => Secret::Pwd(Mask::from(env::var(Defaults::Pwd.to_string())?)),
128 },
129 };
130 let mut host = match host {
131 Some(val) => val,
132 None => Url::parse(&env::var(Defaults::Host.to_string())?)?,
133 };
134 let port = match port {
135 Some(p) => Some(p),
136 None => env::var(Defaults::Port.to_string())
137 .ok()
138 .and_then(|s| s.parse::<u16>().ok()),
139 };
140 if let Some(p) = port {
141 host.set_port(Some(p)).expect("Should have set the port");
142 }
143 Ok(Profile::new(&username, secret, host))
144 }
145}