Skip to content

Commit 2eab6db

Browse files
YgorSouzahacknus
authored andcommitted
Remove the directories dependency (emilk#4904)
eframe now has its own logic to find the storage_dir to persist the app when the persistence feature is enabled, instead of using the directories crate. The directory should be the same as before (verified with a unit test). * Closes <emilk#4884> * [x] I have followed the instructions in the PR template
1 parent 8722dea commit 2eab6db

File tree

5 files changed

+107
-11
lines changed

5 files changed

+107
-11
lines changed

Cargo.lock

+5-3
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,7 @@ dependencies = [
11801180
"glow",
11811181
"glutin",
11821182
"glutin-winit",
1183+
"home",
11831184
"image",
11841185
"js-sys",
11851186
"log",
@@ -1200,6 +1201,7 @@ dependencies = [
12001201
"web-time",
12011202
"wgpu",
12021203
"winapi",
1204+
"windows-sys 0.52.0",
12031205
"winit",
12041206
]
12051207

@@ -2059,11 +2061,11 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
20592061

20602062
[[package]]
20612063
name = "home"
2062-
version = "0.5.5"
2064+
version = "0.5.9"
20632065
source = "registry+https://github.com/rust-lang/crates.io-index"
2064-
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
2066+
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
20652067
dependencies = [
2066-
"windows-sys 0.48.0",
2068+
"windows-sys 0.52.0",
20672069
]
20682070

20692071
[[package]]

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ document-features = " 0.2.8"
7777
glow = "0.13"
7878
glutin = "0.32.0"
7979
glutin-winit = "0.5.0"
80+
home = "0.5.9"
8081
image = { version = "0.25", default-features = false }
8182
log = { version = "0.4", features = ["std"] }
8283
nohash-hasher = "0.2"
@@ -95,6 +96,7 @@ wgpu = { version = "22.1.0", default-features = false, features = [
9596
# Make the renderer `Sync` even on wasm32, because it makes the code simpler:
9697
"fragile-send-sync-non-atomic-wasm",
9798
] }
99+
windows-sys = "0.52"
98100
winit = { version = "0.30.5", default-features = false }
99101

100102
[workspace.lints.rust]

crates/eframe/Cargo.toml

+7-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ glow = ["dep:egui_glow", "dep:glow", "dep:glutin-winit", "dep:glutin"]
6363

6464
## Enable saving app state to disk.
6565
persistence = [
66-
"directories",
66+
"dep:home",
6767
"egui-winit/serde",
6868
"egui/persistence",
6969
"ron",
@@ -148,7 +148,6 @@ image = { workspace = true, features = ["png"] } # Needed for app icon
148148
winit = { workspace = true, default-features = false, features = ["rwh_06"] }
149149

150150
# optional native:
151-
directories = { version = "5", optional = true }
152151
egui-wgpu = { workspace = true, optional = true, features = [
153152
"winit",
154153
] } # if wgpu is used, use it with winit
@@ -158,6 +157,7 @@ pollster = { version = "0.3", optional = true } # needed for wgpu
158157
# this can be done at the same time we expose x11/wayland features of winit crate.
159158
glutin = { workspace = true, optional = true }
160159
glutin-winit = { workspace = true, optional = true }
160+
home = { workspace = true, optional = true }
161161
puffin = { workspace = true, optional = true }
162162
wgpu = { workspace = true, optional = true, features = [
163163
# Let's enable some backends so that users can use `eframe` out-of-the-box
@@ -185,6 +185,7 @@ objc2-app-kit = { version = "0.2.0", features = [
185185
# windows:
186186
[target.'cfg(any(target_os = "windows"))'.dependencies]
187187
winapi = { version = "0.3.9", features = ["winuser"] }
188+
windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_System_Com"] }
188189

189190
# -------------------------------------------
190191
# web:
@@ -253,3 +254,7 @@ wgpu = { workspace = true, optional = true, features = [
253254
# without having to explicitly opt-in to backends
254255
"webgpu",
255256
] }
257+
258+
# Native dev dependencies for testing
259+
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
260+
directories = "5"

crates/eframe/src/epi.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ pub struct NativeOptions {
351351
/// persisted (only if the "persistence" feature is enabled).
352352
pub persist_window: bool,
353353

354-
/// The folder where `eframe` will store the app state. If not set, eframe will get the paths
355-
/// from [directories].
354+
/// The folder where `eframe` will store the app state. If not set, eframe will use a default
355+
/// data storage path for each target system.
356356
pub persistence_path: Option<std::path::PathBuf>,
357357

358358
/// Controls whether to apply dithering to minimize banding artifacts.

crates/eframe/src/native/file_storage.rs

+91-4
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,80 @@ use std::{
1010
/// [`egui::ViewportBuilder::app_id`] of [`crate::NativeOptions::viewport`]
1111
/// or the title argument to [`crate::run_native`].
1212
///
13-
/// On native the path is picked using [`directories::ProjectDirs::data_dir`](https://docs.rs/directories/5.0.1/directories/struct.ProjectDirs.html#method.data_dir) which is:
13+
/// On native, the path is:
1414
/// * Linux: `/home/UserName/.local/share/APP_ID`
1515
/// * macOS: `/Users/UserName/Library/Application Support/APP_ID`
16-
/// * Windows: `C:\Users\UserName\AppData\Roaming\APP_ID`
16+
/// * Windows: `C:\Users\UserName\AppData\Roaming\APP_ID\data`
1717
pub fn storage_dir(app_id: &str) -> Option<PathBuf> {
18-
directories::ProjectDirs::from("", "", app_id)
19-
.map(|proj_dirs| proj_dirs.data_dir().to_path_buf())
18+
use egui::os::OperatingSystem as OS;
19+
use std::env::var_os;
20+
match OS::from_target_os() {
21+
OS::Nix => var_os("XDG_DATA_HOME")
22+
.map(PathBuf::from)
23+
.filter(|p| p.is_absolute())
24+
.or_else(|| home::home_dir().map(|p| p.join(".local").join("share")))
25+
.map(|p| {
26+
p.join(
27+
app_id
28+
.to_lowercase()
29+
.replace(|c: char| c.is_ascii_whitespace(), ""),
30+
)
31+
}),
32+
OS::Mac => home::home_dir().map(|p| {
33+
p.join("Library")
34+
.join("Application Support")
35+
.join(app_id.replace(|c: char| c.is_ascii_whitespace(), "-"))
36+
}),
37+
OS::Windows => roaming_appdata().map(|p| p.join(app_id).join("data")),
38+
OS::Unknown | OS::Android | OS::IOS => None,
39+
}
40+
}
41+
42+
// Adapted from
43+
// https://github.com/rust-lang/cargo/blob/6e11c77384989726bb4f412a0e23b59c27222c34/crates/home/src/windows.rs#L19-L37
44+
#[cfg(all(windows, not(target_vendor = "uwp")))]
45+
#[allow(unsafe_code)]
46+
fn roaming_appdata() -> Option<PathBuf> {
47+
use std::ffi::OsString;
48+
use std::os::windows::ffi::OsStringExt;
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+
58+
extern "C" {
59+
fn wcslen(buf: *const u16) -> usize;
60+
}
61+
unsafe {
62+
let mut path = ptr::null_mut();
63+
match SHGetKnownFolderPath(
64+
&FOLDERID_RoamingAppData,
65+
KF_FLAG_DONT_VERIFY as u32,
66+
0,
67+
&mut path,
68+
) {
69+
S_OK => {
70+
let path_slice = slice::from_raw_parts(path, wcslen(path));
71+
let s = OsString::from_wide(&path_slice);
72+
CoTaskMemFree(path.cast());
73+
Some(PathBuf::from(s))
74+
}
75+
_ => {
76+
// Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`.
77+
CoTaskMemFree(path.cast());
78+
None
79+
}
80+
}
81+
}
82+
}
83+
84+
#[cfg(any(not(windows), target_vendor = "uwp"))]
85+
fn roaming_appdata() -> Option<PathBuf> {
86+
None
2087
}
2188

2289
// ----------------------------------------------------------------------------
@@ -171,3 +238,23 @@ where
171238
}
172239
}
173240
}
241+
242+
#[cfg(test)]
243+
mod tests {
244+
use super::*;
245+
246+
fn directories_storage_dir(app_id: &str) -> Option<PathBuf> {
247+
directories::ProjectDirs::from("", "", app_id)
248+
.map(|proj_dirs| proj_dirs.data_dir().to_path_buf())
249+
}
250+
251+
#[test]
252+
fn storage_path_matches_directories() {
253+
use super::storage_dir;
254+
for app_id in [
255+
"MyApp", "My App", "my_app", "my-app", "My.App", "my/app", "my:app", r"my\app",
256+
] {
257+
assert_eq!(directories_storage_dir(app_id), storage_dir(app_id));
258+
}
259+
}
260+
}

0 commit comments

Comments
 (0)