1
1
use anyhow:: Result ;
2
2
use crossterm:: event:: { KeyCode , KeyModifiers } ;
3
- use std:: { path:: PathBuf , rc:: Rc } ;
3
+ use std:: { fs :: canonicalize , path:: PathBuf , rc:: Rc } ;
4
4
5
5
use crate :: { args:: get_app_config_path, strings:: symbol} ;
6
6
@@ -10,6 +10,8 @@ use super::{
10
10
} ;
11
11
12
12
pub type SharedKeyConfig = Rc < KeyConfig > ;
13
+ const KEY_LIST_FILENAME : & str = "key_bindings.ron" ;
14
+ const KEY_SYMBOLS_FILENAME : & str = "key_symbols.ron" ;
13
15
14
16
#[ derive( Default , Clone ) ]
15
17
pub struct KeyConfig {
@@ -20,12 +22,16 @@ pub struct KeyConfig {
20
22
impl KeyConfig {
21
23
fn get_config_file ( ) -> Result < PathBuf > {
22
24
let app_home = get_app_config_path ( ) ?;
23
- Ok ( app_home. join ( "key_bindings.ron" ) )
25
+ let config_file = app_home. join ( KEY_LIST_FILENAME ) ;
26
+ canonicalize ( & config_file)
27
+ . map_or_else ( |_| Ok ( config_file) , Ok )
24
28
}
25
29
26
30
fn get_symbols_file ( ) -> Result < PathBuf > {
27
31
let app_home = get_app_config_path ( ) ?;
28
- Ok ( app_home. join ( "key_symbols.ron" ) )
32
+ let symbols_file = app_home. join ( KEY_SYMBOLS_FILENAME ) ;
33
+ canonicalize ( & symbols_file)
34
+ . map_or_else ( |_| Ok ( symbols_file) , Ok )
29
35
}
30
36
31
37
pub fn init ( ) -> Result < Self > {
@@ -114,6 +120,9 @@ impl KeyConfig {
114
120
mod tests {
115
121
use super :: * ;
116
122
use crossterm:: event:: { KeyCode , KeyModifiers } ;
123
+ use std:: fs;
124
+ use std:: io:: Write ;
125
+ use tempfile:: NamedTempFile ;
117
126
118
127
#[ test]
119
128
fn test_get_hint ( ) {
@@ -124,4 +133,147 @@ mod tests {
124
133
) ) ;
125
134
assert_eq ! ( h, "^c" ) ;
126
135
}
136
+
137
+ #[ test]
138
+ fn test_symbolic_links ( ) {
139
+ let app_home = get_app_config_path ( ) . unwrap ( ) ;
140
+ // save current config
141
+ let original_key_list_path = app_home. join ( KEY_LIST_FILENAME ) ;
142
+ let renamed_key_list = if original_key_list_path. exists ( ) {
143
+ let temp = NamedTempFile :: new_in ( & app_home) . unwrap ( ) ;
144
+ fs:: rename ( & original_key_list_path, & temp) . unwrap ( ) ;
145
+ Some ( temp)
146
+ } else {
147
+ None
148
+ } ;
149
+ let original_key_symbols_path =
150
+ app_home. join ( KEY_SYMBOLS_FILENAME ) ;
151
+ let renamed_key_symbols = if original_key_symbols_path
152
+ . exists ( )
153
+ {
154
+ let temp = NamedTempFile :: new_in ( & app_home) . unwrap ( ) ;
155
+ fs:: rename ( & original_key_symbols_path, & temp) . unwrap ( ) ;
156
+ Some ( temp)
157
+ } else {
158
+ None
159
+ } ;
160
+
161
+ // create temporary config files
162
+ let mut temporary_key_list =
163
+ NamedTempFile :: new_in ( & app_home) . unwrap ( ) ;
164
+ writeln ! (
165
+ temporary_key_list,
166
+ r"
167
+ (
168
+ move_down: Some(( code: Char('j'), modifiers: ( bits: 2,),)),
169
+ )
170
+ "
171
+ )
172
+ . unwrap ( ) ;
173
+
174
+ let mut temporary_key_symbols =
175
+ NamedTempFile :: new_in ( & app_home) . unwrap ( ) ;
176
+ writeln ! (
177
+ temporary_key_symbols,
178
+ "
179
+ (
180
+ esc: Some(\" Esc\" ),
181
+ )
182
+ "
183
+ )
184
+ . unwrap ( ) ;
185
+
186
+ // testing
187
+ let result = std:: panic:: catch_unwind ( || {
188
+ let loaded_config = KeyConfig :: init ( ) . unwrap ( ) ;
189
+ assert_eq ! (
190
+ loaded_config. keys. move_down,
191
+ KeysList :: default ( ) . move_down
192
+ ) ;
193
+ assert_eq ! (
194
+ loaded_config. symbols. esc,
195
+ KeySymbols :: default ( ) . esc
196
+ ) ;
197
+
198
+ create_symlink (
199
+ & temporary_key_symbols,
200
+ & original_key_symbols_path,
201
+ )
202
+ . unwrap ( ) ;
203
+ let loaded_config = KeyConfig :: init ( ) . unwrap ( ) ;
204
+ assert_eq ! (
205
+ loaded_config. keys. move_down,
206
+ KeysList :: default ( ) . move_down
207
+ ) ;
208
+ assert_eq ! ( loaded_config. symbols. esc, "Esc" ) ;
209
+
210
+ create_symlink (
211
+ & temporary_key_list,
212
+ & original_key_list_path,
213
+ )
214
+ . unwrap ( ) ;
215
+ let loaded_config = KeyConfig :: init ( ) . unwrap ( ) ;
216
+ assert_eq ! (
217
+ loaded_config. keys. move_down,
218
+ GituiKeyEvent :: new(
219
+ KeyCode :: Char ( 'j' ) ,
220
+ KeyModifiers :: CONTROL
221
+ )
222
+ ) ;
223
+ assert_eq ! ( loaded_config. symbols. esc, "Esc" ) ;
224
+
225
+ fs:: remove_file ( & original_key_symbols_path) . unwrap ( ) ;
226
+ let loaded_config = KeyConfig :: init ( ) . unwrap ( ) ;
227
+ assert_eq ! (
228
+ loaded_config. keys. move_down,
229
+ GituiKeyEvent :: new(
230
+ KeyCode :: Char ( 'j' ) ,
231
+ KeyModifiers :: CONTROL
232
+ )
233
+ ) ;
234
+ assert_eq ! (
235
+ loaded_config. symbols. esc,
236
+ KeySymbols :: default ( ) . esc
237
+ ) ;
238
+
239
+ fs:: remove_file ( & original_key_list_path) . unwrap ( ) ;
240
+ } ) ;
241
+
242
+ // remove symlinks from testing if they still exist
243
+ let _ = fs:: remove_file ( & original_key_list_path) ;
244
+ let _ = fs:: remove_file ( & original_key_symbols_path) ;
245
+
246
+ // restore original config files
247
+ if let Some ( temp) = renamed_key_list {
248
+ let _ = fs:: rename ( & temp, & original_key_list_path) ;
249
+ }
250
+
251
+ if let Some ( temp) = renamed_key_symbols {
252
+ let _ = fs:: rename ( & temp, & original_key_symbols_path) ;
253
+ }
254
+
255
+ assert ! ( result. is_ok( ) ) ;
256
+ }
257
+
258
+ #[ cfg( not( target_os = "windows" ) ) ]
259
+ fn create_symlink <
260
+ P : AsRef < std:: path:: Path > ,
261
+ Q : AsRef < std:: path:: Path > ,
262
+ > (
263
+ original : P ,
264
+ link : Q ,
265
+ ) -> Result < ( ) , std:: io:: Error > {
266
+ std:: os:: unix:: fs:: symlink ( original, link)
267
+ }
268
+
269
+ #[ cfg( target_os = "windows" ) ]
270
+ fn create_symlink <
271
+ P : AsRef < std:: path:: Path > ,
272
+ Q : AsRef < std:: path:: Path > ,
273
+ > (
274
+ original : P ,
275
+ link : Q ,
276
+ ) -> Result < ( ) , std:: io:: Error > {
277
+ std:: os:: windows:: fs:: symlink_file ( original, link)
278
+ }
127
279
}
0 commit comments