veloren_common_base/userdata_dir.rs
1use std::path::PathBuf;
2use tracing::warn;
3
4const VELOREN_USERDATA_ENV: &str = "VELOREN_USERDATA";
5
6// TODO: consider expanding this to a general install strategy variable that is
7// also used for finding assets
8// TODO: Ensure there are no NUL (\0) characters in userdata_dir (possible on
9// MacOS but not Windows or Linux) as SQLite requires the database path does not
10// include this character.
11/// Determines common user data directory used by veloren frontends.
12///
13/// The first specified in this list is used.
14/// 1. The VELOREN_USERDATA runtime environment variable
15/// 2. The VELOREN_USERDATA_STRATEGY compile time environment variable
16/// 3. The CARGO_WORKSPACE_DIR/userdata compile time environment variable
17/// defined in .cargo/config.toml
18///
19/// ### `VELOREN_USERDATA_STRATEGY` environment variable
20///
21/// Read during compilation and useful to set when compiling for distribution.
22///
23/// Available options are:
24/// * "system" => system specific project data directory
25/// * "executable" => `<executable dir>/userdata`
26///
27/// > **Note:** _case insensitive_
28pub fn userdata_dir() -> PathBuf {
29 // 1. The VELOREN_USERDATA runtime environment variable
30 std::env::var_os(VELOREN_USERDATA_ENV)
31 .map(PathBuf::from)
32 // 2. The VELOREN_USERDATA_STRATEGY compile time environment variable
33 .or_else(|| match option_env!("VELOREN_USERDATA_STRATEGY") {
34 // "system" => system specific project data directory
35 Some(s) if s.eq_ignore_ascii_case("system") => Some(directories_next::ProjectDirs::from("net", "veloren", "veloren")
36 .expect("System's $HOME directory path not found!")
37 .data_dir()
38 .join("userdata")
39 ),
40 // "executable" => <executable dir>/userdata
41 Some(s) if s.eq_ignore_ascii_case("executable") => {
42 let mut path = std::env::current_exe()
43 .expect("Failed to retrieve executable path!");
44 path.pop();
45 path.push("userdata");
46 Some(path)
47 },
48 Some(s) => {
49 warn!(
50 "Compiled with an invalid VELOREN_USERDATA_STRATEGY: \"{}\". \
51 Valid values are unset, \"system\", and \"executable\". \
52 Falling back to unset case.",
53 s,
54 );
55 None
56 },
57 _ => None,
58 })
59 // 3. The CARGO_WORKSPACE_DIR/userdata compile time environment variable
60 // defined in .cargo/config.toml
61 .unwrap_or_else(|| {
62 let mut path = PathBuf::from(env!("CARGO_WORKSPACE_DIR"));
63 let exe_path = std::env::current_exe()
64 .expect("Failed to retrieve executable path!");
65 // If this path exists
66 // and the binary path is prefixed by this path
67 // put the userdata folder there
68 if path.exists() && exe_path.starts_with(&path) {
69 path.push("userdata");
70 path
71 } else {
72 // otherwise warn and fallback to the executable strategy
73 let project_path = path;
74 let mut path = exe_path;
75 path.pop();
76 path.push("userdata");
77 warn!(
78 "This binary is outside the project folder where it was compiled ({}) \
79 and was not compiled with VELOREN_USERDATA_STRATEGY set to \"system\" or \"executable\". \
80 Falling back the to the \"executable\" strategy (the userdata folder will be placed in the \
81 same folder as the executable: {}) \n\
82 NOTE: You can manually select a userdata folder (overriding this automatic selection) by \
83 setting the environment variable {} to the desired directory before running. \n\
84 NOTE: If you have not moved the executable this can occur when using a custom cargo \
85 target-dir that is not inside the project folder.",
86 project_path.display(),
87 path.display(),
88 VELOREN_USERDATA_ENV,
89 );
90 path
91 }
92 })
93}