diff options
Diffstat (limited to 'src/client/paths.rs')
| -rw-r--r-- | src/client/paths.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/client/paths.rs b/src/client/paths.rs new file mode 100644 index 0000000..b6b8d1f --- /dev/null +++ b/src/client/paths.rs | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | //! Client data location and command-line arguments. | ||
| 2 | |||
| 3 | use std::ffi::OsStr; | ||
| 4 | use std::path::{Path, PathBuf}; | ||
| 5 | |||
| 6 | pub const APP_DIR: &str = "zagan-bet"; | ||
| 7 | pub const DATABASE_FILE: &str = "client.db"; | ||
| 8 | |||
| 9 | pub const USAGE: &str = "\ | ||
| 10 | usage: client [-d <dir>] [--unsafe] | ||
| 11 | |||
| 12 | -d, --data-dir <dir> where to keep client.db (default: XDG data dir) | ||
| 13 | --unsafe skip certificate verification | ||
| 14 | -h, --help show this message | ||
| 15 | |||
| 16 | The database lives in $XDG_DATA_HOME/zagan-bet/client.db, falling back to | ||
| 17 | ~/.local/share/zagan-bet/client.db (or %APPDATA%\\zagan-bet on Windows)."; | ||
| 18 | |||
| 19 | #[derive(Debug, Default, PartialEq, Eq)] | ||
| 20 | pub struct ClientArgs { | ||
| 21 | pub unsafe_mode: bool, | ||
| 22 | pub data_dir: Option<PathBuf>, | ||
| 23 | pub help: bool, | ||
| 24 | } | ||
| 25 | |||
| 26 | /// Parse arguments, without the program name. | ||
| 27 | pub fn parse_args(args: impl IntoIterator<Item = String>) -> Result<ClientArgs, String> { | ||
| 28 | let mut parsed = ClientArgs::default(); | ||
| 29 | let mut args = args.into_iter(); | ||
| 30 | |||
| 31 | while let Some(argument) = args.next() { | ||
| 32 | match argument.as_str() { | ||
| 33 | "-d" | "--data-dir" => { | ||
| 34 | let Some(value) = args.next() else { | ||
| 35 | return Err(format!("{argument} needs a directory\n{USAGE}")); | ||
| 36 | }; | ||
| 37 | if value.is_empty() { | ||
| 38 | return Err(format!("{argument} needs a directory\n{USAGE}")); | ||
| 39 | } | ||
| 40 | parsed.data_dir = Some(PathBuf::from(value)); | ||
| 41 | } | ||
| 42 | "--unsafe" => parsed.unsafe_mode = true, | ||
| 43 | "-h" | "--help" => parsed.help = true, | ||
| 44 | _ => { | ||
| 45 | if let Some(value) = argument.strip_prefix("--data-dir=") { | ||
| 46 | if value.is_empty() { | ||
| 47 | return Err(format!("--data-dir needs a directory\n{USAGE}")); | ||
| 48 | } | ||
| 49 | parsed.data_dir = Some(PathBuf::from(value)); | ||
| 50 | } else { | ||
| 51 | return Err(format!("unknown argument: {argument}\n{USAGE}")); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | Ok(parsed) | ||
| 58 | } | ||
| 59 | |||
| 60 | /// `$XDG_DATA_HOME/zagan-bet`, `%APPDATA%\zagan-bet`, or | ||
| 61 | /// `$HOME/.local/share/zagan-bet`, in that order. | ||
| 62 | pub fn data_dir_from( | ||
| 63 | xdg_data_home: Option<&OsStr>, | ||
| 64 | home: Option<&OsStr>, | ||
| 65 | appdata: Option<&OsStr>, | ||
| 66 | ) -> PathBuf { | ||
| 67 | if let Some(xdg) = xdg_data_home { | ||
| 68 | let path = Path::new(xdg); | ||
| 69 | // The XDG spec says a relative XDG_DATA_HOME is invalid. | ||
| 70 | if !xdg.is_empty() && path.is_absolute() { | ||
| 71 | return path.join(APP_DIR); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | if let Some(appdata) = appdata { | ||
| 76 | let path = Path::new(appdata); | ||
| 77 | if !appdata.is_empty() && path.is_absolute() { | ||
| 78 | return path.join(APP_DIR); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | if let Some(home) = home { | ||
| 83 | let path = Path::new(home); | ||
| 84 | if !home.is_empty() && path.is_absolute() { | ||
| 85 | return path.join(".local").join("share").join(APP_DIR); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | // No usable home directory: keep the old behaviour and stay local. | ||
| 90 | PathBuf::from(APP_DIR) | ||
| 91 | } | ||
| 92 | |||
| 93 | pub fn default_data_dir() -> PathBuf { | ||
| 94 | data_dir_from( | ||
| 95 | std::env::var_os("XDG_DATA_HOME").as_deref(), | ||
| 96 | std::env::var_os("HOME").as_deref(), | ||
| 97 | std::env::var_os("APPDATA").as_deref(), | ||
| 98 | ) | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn database_path(data_dir: &Path) -> PathBuf { | ||
| 102 | data_dir.join(DATABASE_FILE) | ||
| 103 | } | ||
