@@ -10,60 +10,78 @@ use std::{
10
10
/// [`egui::ViewportBuilder::app_id`] of [`crate::NativeOptions::viewport`]
11
11
/// or the title argument to [`crate::run_native`].
12
12
///
13
- /// On native, if the `directories` feature is enabled, the path is picked using
14
- /// [`directories::ProjectDirs::data_dir`](https://docs.rs/directories/5.0.1/directories/struct.ProjectDirs.html#method.data_dir)
15
- /// which is:
13
+ /// On native, the path is:
16
14
/// * Linux: `/home/UserName/.local/share/APP_ID`
17
15
/// * macOS: `/Users/UserName/Library/Application Support/APP_ID`
18
16
/// * Windows: `C:\Users\UserName\AppData\Roaming\APP_ID\data`
19
- ///
20
- /// If the `directories` feature is not enabled, it uses a naive approximation that returns the
21
- /// same result for the most common systems.
22
17
pub fn storage_dir ( app_id : & str ) -> Option < PathBuf > {
23
- #[ cfg( feature = "directories" ) ]
24
- {
25
- directories_storage_dir ( app_id)
26
- }
27
- #[ cfg( not( feature = "directories" ) ) ]
28
- {
29
- naive_storage_dir ( app_id)
30
- }
31
- }
32
-
33
- #[ cfg( feature = "directories" ) ]
34
- #[ inline]
35
- fn directories_storage_dir ( app_id : & str ) -> Option < PathBuf > {
36
- directories:: ProjectDirs :: from ( "" , "" , app_id)
37
- . map ( |proj_dirs| proj_dirs. data_dir ( ) . to_path_buf ( ) )
38
- }
39
-
40
- #[ allow( dead_code) ]
41
- #[ inline]
42
- fn naive_storage_dir ( app_id : & str ) -> Option < PathBuf > {
43
18
use egui:: os:: OperatingSystem as OS ;
44
19
use std:: env:: var_os;
45
20
match OS :: from_target_os ( ) {
46
21
OS :: Nix => var_os ( "XDG_DATA_HOME" )
47
22
. map ( PathBuf :: from)
48
- . or_else ( || var_os ( "HOME" ) . map ( |s| PathBuf :: from ( s) . join ( ".local" ) . join ( "share" ) ) )
23
+ . filter ( |p| p. is_absolute ( ) )
24
+ . or_else ( || home:: home_dir ( ) . map ( |p| p. join ( ".local" ) . join ( "share" ) ) )
49
25
. map ( |p| {
50
26
p. join (
51
27
app_id
52
28
. to_lowercase ( )
53
29
. replace ( |c : char | c. is_ascii_whitespace ( ) , "" ) ,
54
30
)
55
31
} ) ,
56
- OS :: Mac => var_os ( "HOME" ) . map ( |s| {
57
- PathBuf :: from ( s)
58
- . join ( "Library" )
32
+ OS :: Mac => home:: home_dir ( ) . map ( |p| {
33
+ p. join ( "Library" )
59
34
. join ( "Application Support" )
60
35
. join ( app_id. replace ( |c : char | c. is_ascii_whitespace ( ) , "-" ) )
61
36
} ) ,
62
- OS :: Windows => var_os ( "APPDATA" ) . map ( |s| PathBuf :: from ( s ) . join ( app_id) . join ( "data" ) ) ,
37
+ OS :: Windows => roaming_appdata ( ) . map ( |p| p . join ( app_id) . join ( "data" ) ) ,
63
38
OS :: Unknown | OS :: Android | OS :: IOS => None ,
64
39
}
65
40
}
66
41
42
+ // Adapted from
43
+ // https://github.com/rust-lang/cargo/blob/59ecb11a298f0c3740216220361024cf05ed5b5a/crates/home/src/windows.rs#L19-L42
44
+ #[ cfg( all( windows, not( target_vendor = "uwp" ) ) ) ]
45
+ fn roaming_appdata ( ) -> Option < PathBuf > {
46
+ use std:: ffi:: OsString ;
47
+ use std:: os:: windows:: ffi:: OsStringExt ;
48
+ use std:: path:: PathBuf ;
49
+ use std:: ptr;
50
+ use std:: slice;
51
+
52
+ use windows_sys:: Win32 :: Foundation :: S_OK ;
53
+ use windows_sys:: Win32 :: System :: Com :: CoTaskMemFree ;
54
+ use windows_sys:: Win32 :: UI :: Shell :: {
55
+ FOLDERID_RoamingAppData , SHGetKnownFolderPath , KF_FLAG_DONT_VERIFY ,
56
+ } ;
57
+ unsafe {
58
+ let mut path = ptr:: null_mut ( ) ;
59
+ match SHGetKnownFolderPath (
60
+ & FOLDERID_RoamingAppData ,
61
+ KF_FLAG_DONT_VERIFY as u32 ,
62
+ std:: ptr:: null_mut ( ) ,
63
+ & mut path,
64
+ ) {
65
+ S_OK => {
66
+ let path_slice = slice:: from_raw_parts ( path, wcslen ( path) ) ;
67
+ let s = OsString :: from_wide ( & path_slice) ;
68
+ CoTaskMemFree ( path. cast ( ) ) ;
69
+ Some ( PathBuf :: from ( s) )
70
+ }
71
+ _ => {
72
+ // Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`.
73
+ CoTaskMemFree ( path. cast ( ) ) ;
74
+ None
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ #[ cfg( any( not( windows) , target_vendor = "uwp" ) ) ]
81
+ fn roaming_appdata ( ) -> Option < PathBuf > {
82
+ None
83
+ }
84
+
67
85
// ----------------------------------------------------------------------------
68
86
69
87
/// A key-value store backed by a [RON](https://github.com/ron-rs/ron) file on disk.
@@ -219,14 +237,20 @@ where
219
237
220
238
#[ cfg( test) ]
221
239
mod tests {
222
- #[ cfg( feature = "directories" ) ]
240
+ use super :: * ;
241
+
242
+ fn directories_storage_dir ( app_id : & str ) -> Option < PathBuf > {
243
+ directories:: ProjectDirs :: from ( "" , "" , app_id)
244
+ . map ( |proj_dirs| proj_dirs. data_dir ( ) . to_path_buf ( ) )
245
+ }
246
+
223
247
#[ test]
224
- fn naive_path_matches_directories ( ) {
225
- use super :: { directories_storage_dir , naive_storage_dir } ;
248
+ fn storage_path_matches_directories ( ) {
249
+ use super :: storage_dir ;
226
250
for app_id in [
227
251
"MyApp" , "My App" , "my_app" , "my-app" , "My.App" , "my/app" , "my:app" , r"my\app" ,
228
252
] {
229
- assert_eq ! ( directories_storage_dir( app_id) , naive_storage_dir ( app_id) ) ;
253
+ assert_eq ! ( directories_storage_dir( app_id) , storage_dir ( app_id) ) ;
230
254
}
231
255
}
232
256
}
0 commit comments