From a036e7eb46886ff7fe88255ae26c327f63ec4bfa Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 16:26:21 +0100 Subject: v3.3.0: keep client.db in the XDG data dir, add -d/--data-dir --- src/client/mod.rs | 1 + src/client/paths.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/client/paths.rs (limited to 'src/client') diff --git a/src/client/mod.rs b/src/client/mod.rs index dc8423b..6841be3 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,4 +1,5 @@ pub mod core; pub mod database; pub mod games; +pub mod paths; pub mod ui; 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 @@ +//! Client data location and command-line arguments. + +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; + +pub const APP_DIR: &str = "zagan-bet"; +pub const DATABASE_FILE: &str = "client.db"; + +pub const USAGE: &str = "\ +usage: client [-d ] [--unsafe] + + -d, --data-dir where to keep client.db (default: XDG data dir) + --unsafe skip certificate verification + -h, --help show this message + +The database lives in $XDG_DATA_HOME/zagan-bet/client.db, falling back to +~/.local/share/zagan-bet/client.db (or %APPDATA%\\zagan-bet on Windows)."; + +#[derive(Debug, Default, PartialEq, Eq)] +pub struct ClientArgs { + pub unsafe_mode: bool, + pub data_dir: Option, + pub help: bool, +} + +/// Parse arguments, without the program name. +pub fn parse_args(args: impl IntoIterator) -> Result { + let mut parsed = ClientArgs::default(); + let mut args = args.into_iter(); + + while let Some(argument) = args.next() { + match argument.as_str() { + "-d" | "--data-dir" => { + let Some(value) = args.next() else { + return Err(format!("{argument} needs a directory\n{USAGE}")); + }; + if value.is_empty() { + return Err(format!("{argument} needs a directory\n{USAGE}")); + } + parsed.data_dir = Some(PathBuf::from(value)); + } + "--unsafe" => parsed.unsafe_mode = true, + "-h" | "--help" => parsed.help = true, + _ => { + if let Some(value) = argument.strip_prefix("--data-dir=") { + if value.is_empty() { + return Err(format!("--data-dir needs a directory\n{USAGE}")); + } + parsed.data_dir = Some(PathBuf::from(value)); + } else { + return Err(format!("unknown argument: {argument}\n{USAGE}")); + } + } + } + } + + Ok(parsed) +} + +/// `$XDG_DATA_HOME/zagan-bet`, `%APPDATA%\zagan-bet`, or +/// `$HOME/.local/share/zagan-bet`, in that order. +pub fn data_dir_from( + xdg_data_home: Option<&OsStr>, + home: Option<&OsStr>, + appdata: Option<&OsStr>, +) -> PathBuf { + if let Some(xdg) = xdg_data_home { + let path = Path::new(xdg); + // The XDG spec says a relative XDG_DATA_HOME is invalid. + if !xdg.is_empty() && path.is_absolute() { + return path.join(APP_DIR); + } + } + + if let Some(appdata) = appdata { + let path = Path::new(appdata); + if !appdata.is_empty() && path.is_absolute() { + return path.join(APP_DIR); + } + } + + if let Some(home) = home { + let path = Path::new(home); + if !home.is_empty() && path.is_absolute() { + return path.join(".local").join("share").join(APP_DIR); + } + } + + // No usable home directory: keep the old behaviour and stay local. + PathBuf::from(APP_DIR) +} + +pub fn default_data_dir() -> PathBuf { + data_dir_from( + std::env::var_os("XDG_DATA_HOME").as_deref(), + std::env::var_os("HOME").as_deref(), + std::env::var_os("APPDATA").as_deref(), + ) +} + +pub fn database_path(data_dir: &Path) -> PathBuf { + data_dir.join(DATABASE_FILE) +} -- cgit v1.2.3