@@ -5,7 +5,7 @@ use std::path::PathBuf;
5
5
use std:: { collections:: HashMap , path:: Path } ;
6
6
7
7
use anyhow:: { Context , Result } ;
8
- use config:: { Config , File } ;
8
+ use config:: { Config , File , FileFormat } ;
9
9
use once_cell:: sync:: Lazy ;
10
10
use serde:: { Deserialize , Serialize } ;
11
11
@@ -14,16 +14,16 @@ use crate::ctx;
14
14
type ProfileName = String ;
15
15
type AuthScript = String ;
16
16
17
- static CONFIGS_PATH : Lazy < PathBuf > = Lazy :: new ( || {
17
+ pub static CONFIGS_PATH : Lazy < PathBuf > = Lazy :: new ( || {
18
18
let mut path = home_dir ( ) . unwrap ( ) ;
19
19
path. push ( ".awsctx/configs.yaml" ) ;
20
20
path
21
21
} ) ;
22
- const CONFIGS_DESCRIPTIONS : & str = r#"# # Configurations for awsctx
22
+ const CONFIGS_DESCRIPTIONS : & str = r#"# # Configurations for awsctx
23
23
# # You can manually edit configurations according to the following usage
24
24
25
25
# # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
26
- # auth_commands:
26
+ # auth_commands:
27
27
# # configuration for `foo` profile with aws configure
28
28
# foo: |
29
29
# # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
@@ -35,7 +35,7 @@ const CONFIGS_DESCRIPTIONS: &str = r#"# # Configurations for awsctx
35
35
# onelogin-aws-login -C {{profile}} --profile {{profile}} -u [email protected]
36
36
"# ;
37
37
38
- #[ derive( Debug , Serialize , Deserialize , Default ) ]
38
+ #[ derive( Debug , Serialize , Deserialize , Default , PartialEq , Eq ) ]
39
39
pub struct Configs {
40
40
pub auth_commands : HashMap < ProfileName , AuthScript > ,
41
41
}
@@ -46,17 +46,16 @@ impl Configs {
46
46
. map ( |p| p. as_ref ( ) . to_path_buf ( ) )
47
47
. unwrap_or_else ( || CONFIGS_PATH . clone ( ) ) ;
48
48
let c = Config :: builder ( )
49
- . add_source ( File :: with_name ( path. to_str ( ) . unwrap ( ) ) )
49
+ . add_source ( File :: new ( path. to_str ( ) . unwrap ( ) , FileFormat :: Yaml ) )
50
50
. build ( )
51
51
. context ( format ! (
52
52
"failed to build configuration from path: {}" ,
53
53
path. to_str( ) . unwrap( )
54
54
) )
55
55
. map_err ( |e| ctx:: CTXError :: InvalidConfigurations {
56
- message : format ! (
57
- "failed to load configurations, check your configurations ({})" ,
58
- path. to_str( ) . unwrap( )
59
- ) ,
56
+ message :
57
+ "failed to load configurations, check your configurations (~/.aws/configs.yaml)"
58
+ . to_string ( ) ,
60
59
source : Some ( e) ,
61
60
} ) ?;
62
61
@@ -66,16 +65,17 @@ impl Configs {
66
65
path. to_str( ) . unwrap( )
67
66
) )
68
67
. map_err ( |e| ctx:: CTXError :: InvalidConfigurations {
69
- message : format ! (
70
- "failed to deserialize configurations, check your configurations ({})" ,
71
- path. to_str( ) . unwrap( )
72
- ) ,
68
+ message : "failed to deserialize configurations, check your configurations (~/.aws/configs.yaml)" . to_string ( ) ,
73
69
source : Some ( e) ,
74
70
} )
75
71
}
76
72
77
- pub fn initialize_default_configs ( ) -> Result < Self , ctx:: CTXError > {
78
- let path: & PathBuf = & CONFIGS_PATH ;
73
+ pub fn initialize_default_configs < P : AsRef < Path > > (
74
+ path : Option < P > ,
75
+ ) -> Result < Self , ctx:: CTXError > {
76
+ let path = path
77
+ . map ( |p| p. as_ref ( ) . to_path_buf ( ) )
78
+ . unwrap_or_else ( || CONFIGS_PATH . clone ( ) ) ;
79
79
if path. exists ( ) {
80
80
return Self :: load_configs ( Some ( path) ) ;
81
81
}
@@ -87,7 +87,7 @@ impl Configs {
87
87
None => ( ) ,
88
88
}
89
89
let c = Configs :: default ( ) ;
90
- let mut file = fs:: File :: create ( path)
90
+ let mut file = fs:: File :: create ( & path)
91
91
. context ( "failed to create a configuration file" )
92
92
. map_err ( |e| ctx:: CTXError :: UnexpectedError { source : Some ( e) } ) ?;
93
93
file. write_all ( CONFIGS_DESCRIPTIONS . as_bytes ( ) )
@@ -104,3 +104,123 @@ impl Configs {
104
104
Self :: load_configs ( Some ( path) )
105
105
}
106
106
}
107
+
108
+ #[ cfg( test) ]
109
+ mod tests {
110
+ use std:: io:: { Seek , SeekFrom } ;
111
+
112
+ use rstest:: * ;
113
+ use tempfile:: { NamedTempFile , TempDir } ;
114
+
115
+ use super :: * ;
116
+
117
+ #[ fixture]
118
+ pub fn configs_text ( ) -> String {
119
+ r#"# # Configurations for awsctx
120
+ # # You can manually edit configurations according to the following usage
121
+
122
+ # # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
123
+ # auth_commands:
124
+ # # configuration for `foo` profile with aws configure
125
+ # foo: |
126
+ # # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
127
+ # # In this case, `{{profile}}` is replaced by `foo`
128
+ # aws configure --profile {{profile}}
129
+ # # configuration for `bar` profile with [onelogin-aws-cli](https://github.com/physera/onelogin-aws-cli)
130
+ # bar: |
131
+ # # In this case, name of one-login configuration is same as `profile`
132
+ # onelogin-aws-login -C {{profile}} --profile {{profile}} -u [email protected]
133
+ ---
134
+ auth_commands:
135
+ foo: |
136
+ echo 1"#
137
+ . to_string ( )
138
+ }
139
+
140
+ #[ fixture]
141
+ pub fn configs_file ( configs_text : String ) -> NamedTempFile {
142
+ let mut f = NamedTempFile :: new ( ) . unwrap ( ) ;
143
+ write ! ( f, "{}" , configs_text) . unwrap ( ) ;
144
+ f. flush ( ) . unwrap ( ) ;
145
+ f. seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
146
+ f
147
+ }
148
+
149
+ #[ fixture]
150
+ pub fn configs ( ) -> Configs {
151
+ Configs {
152
+ auth_commands : vec ! [ ( "foo" . to_string( ) , "echo 1" . to_string( ) ) ]
153
+ . into_iter ( )
154
+ . collect :: < HashMap < String , String > > ( ) ,
155
+ }
156
+ }
157
+
158
+ #[ rstest( input, expect) ]
159
+ #[ case( configs_file( configs_text( ) ) , Ok ( configs( ) ) ) ]
160
+ #[ case(
161
+ configs_file( "invalid_yaml_format: a:a:" . to_string( ) ) ,
162
+ Err (
163
+ ctx:: CTXError :: InvalidConfigurations {
164
+ message: "failed to load configurations, check your configurations (~/.aws/configs.yaml)" . to_string( ) ,
165
+ source: None
166
+ }
167
+ )
168
+ ) ]
169
+ #[ case(
170
+ configs_file( "unknown_key: foo" . to_string( ) ) ,
171
+ Err (
172
+ ctx:: CTXError :: InvalidConfigurations {
173
+ message: "failed to deserialize configurations, check your configurations (~/.aws/configs.yaml)" . to_string( ) ,
174
+ source: None
175
+ }
176
+ )
177
+ ) ]
178
+ fn test_configs_load_configs ( input : NamedTempFile , expect : Result < Configs , ctx:: CTXError > ) {
179
+ let actual = Configs :: load_configs ( Some ( input. path ( ) ) ) ;
180
+ match ( expect, actual) {
181
+ ( Ok ( expect) , Ok ( actual) ) => {
182
+ assert_eq ! ( expect, actual) ;
183
+ }
184
+ ( Err ( expect) , Err ( actual) ) => match ( & expect, & actual) {
185
+ (
186
+ ctx:: CTXError :: InvalidConfigurations {
187
+ message : expect_message,
188
+ source : _,
189
+ } ,
190
+ ctx:: CTXError :: InvalidConfigurations {
191
+ message : actual_message,
192
+ source : _,
193
+ } ,
194
+ ) => assert_eq ! ( expect_message, actual_message) ,
195
+ _ => panic ! ( "unexpected error" ) ,
196
+ } ,
197
+ _ => panic ! ( "expect and actual are not match" ) ,
198
+ }
199
+ }
200
+
201
+ #[ rstest]
202
+ fn test_initialize_default_configs ( ) {
203
+ let tmpdir = TempDir :: new ( ) . unwrap ( ) ;
204
+ let tmpfile = tmpdir. path ( ) . join ( "configs.yaml" ) ;
205
+ Configs :: initialize_default_configs ( Some ( & tmpfile) ) . unwrap ( ) ;
206
+ let expect = r#"# # Configurations for awsctx
207
+ # # You can manually edit configurations according to the following usage
208
+
209
+ # # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
210
+ # auth_commands:
211
+ # # configuration for `foo` profile with aws configure
212
+ # foo: |
213
+ # # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
214
+ # # In this case, `{{profile}}` is replaced by `foo`
215
+ # aws configure --profile {{profile}}
216
+ # # configuration for `bar` profile with [onelogin-aws-cli](https://github.com/physera/onelogin-aws-cli)
217
+ # bar: |
218
+ # # In this case, name of one-login configuration is same as `profile`
219
+ # onelogin-aws-login -C {{profile}} --profile {{profile}} -u [email protected]
220
+ ---
221
+ auth_commands: {}
222
+ "# ;
223
+ let actual = fs:: read_to_string ( tmpfile) . unwrap ( ) ;
224
+ assert_eq ! ( expect, actual) ;
225
+ }
226
+ }
0 commit comments