1use std::{path::Path, str::FromStr};
2
3use crate::{Configuration, Error, Result, authentication::TokenExpiration, error::ResultExt};
4
5const TRANSIP_API_PRIVATE_KEY: &str = "TRANSIP_API_PRIVATE_KEY";
6const TRANSIP_API_USERNAME: &str = "TRANSIP_API_USERNAME";
7const TRANSIP_API_TOKEN_PATH: &str = "TRANSIP_API_TOKEN_PATH";
8const TRANSIP_API_WHITELISTED_ONLY: &str = "TRANSIP_API_WHITELISTED_ONLY";
9const TRANSIP_API_READONLY: &str = "TRANSIP_API_READONLY";
10const TRANSIP_API_IPV6ONLY: &str = "TRANSIP_API_IPV6ONLY";
11const TRANSIP_API_TOKEN_EXPIRATION: &str = "TRANSIP_API_TOKEN_EXPIRATION";
12
13const ENVIRONMENT_VARIABLES: [&str; 7] = [
14 TRANSIP_API_USERNAME,
15 TRANSIP_API_PRIVATE_KEY,
16 TRANSIP_API_TOKEN_PATH,
17 TRANSIP_API_WHITELISTED_ONLY,
18 TRANSIP_API_READONLY,
19 TRANSIP_API_IPV6ONLY,
20 TRANSIP_API_TOKEN_EXPIRATION,
21];
22
23struct Environment {
24 user_name: String,
25 private_key: String,
26 token_path: String,
27 whitelisted_only: bool,
28 read_only: bool,
29 ipv6_only: bool,
30 token_expiration: TokenExpiration,
31}
32
33impl Configuration for Environment {
34 fn user_name(&self) -> &str {
35 self.user_name.as_str()
36 }
37
38 fn private_key_pem_file(&self) -> &str {
39 self.private_key.as_str()
40 }
41
42 fn token_path(&self) -> &str {
43 self.token_path.as_str()
44 }
45
46 fn whitelisted_only(&self) -> bool {
47 self.whitelisted_only
48 }
49
50 fn read_only(&self) -> bool {
51 self.read_only
52 }
53
54 fn ipv6_only(&self) -> bool {
55 self.ipv6_only
56 }
57
58 fn token_expiration(&self) -> TokenExpiration {
59 self.token_expiration.clone()
60 }
61}
62
63fn var(name: &'static str) -> Result<String> {
64 std::env::var(name).map_err(|_| Error::Key(name))
65}
66
67fn check_environment() -> Result<()> {
68 for variable in ENVIRONMENT_VARIABLES {
69 std::env::var(variable).map_err(|_| Error::EnvironmentVariable(variable.to_owned()))?;
70 }
71 if Path::new(&std::env::var(TRANSIP_API_PRIVATE_KEY).unwrap())
72 .try_exists()
73 .is_err()
74 {
75 return Err(Error::EnvironmentVariable(
76 "Private key not found".to_owned(),
77 ));
78 }
79 if var(TRANSIP_API_WHITELISTED_ONLY)
80 .unwrap()
81 .parse::<bool>()
82 .is_err()
83 {
84 return Err(Error::EnvironmentVariable(format!(
85 "{TRANSIP_API_WHITELISTED_ONLY} should contain true of false"
86 )));
87 }
88 if var(TRANSIP_API_READONLY).unwrap().parse::<bool>().is_err() {
89 return Err(Error::EnvironmentVariable(format!(
90 "{TRANSIP_API_READONLY} should contain true of false"
91 )));
92 }
93 Ok(())
94}
95
96pub fn configuration_from_environment() -> Result<Box<dyn Configuration>> {
98 check_environment()?;
99 Ok(Box::new(Environment {
100 user_name: var(TRANSIP_API_USERNAME)?,
101 private_key: var(TRANSIP_API_PRIVATE_KEY)?,
102 token_path: var(TRANSIP_API_TOKEN_PATH)?,
103 whitelisted_only: var(TRANSIP_API_WHITELISTED_ONLY).and_then(parse::<bool>)?,
104 read_only: var(TRANSIP_API_READONLY).and_then(parse::<bool>)?,
105 ipv6_only: var(TRANSIP_API_IPV6ONLY).and_then(parse::<bool>)?,
106 token_expiration: var(TRANSIP_API_TOKEN_EXPIRATION).and_then(parse::<TokenExpiration>)?,
107 }))
108}
109
110fn parse<T>(s: String) -> Result<T>
111where
112 T: FromStr,
113 <T as FromStr>::Err: Into<Error>,
114{
115 s.parse::<T>().err_into()
116}
117
118pub fn demo_configuration() -> Box<dyn Configuration> {
120 Box::new(Environment {
121 user_name: Default::default(),
122 private_key: Default::default(),
123 token_path: Default::default(),
124 read_only: false,
125 ipv6_only: false,
126 whitelisted_only: false,
127 token_expiration: TokenExpiration::Hours(10),
128 })
129}