use std::path::{Path, PathBuf};
pub const APP_DIR: &str = "linkmarks";
pub const STORE_FILENAME: &str = "store.db";
pub const CONFIG_FILENAME: &str = "config.toml";
#[must_use]
pub fn linkmarks_data_dir() -> PathBuf {
if let Some(xdg) = std::env::var_os("XDG_DATA_HOME") {
let trimmed = xdg.to_string_lossy().trim().to_string();
if !trimmed.is_empty() {
return PathBuf::from(trimmed).join(APP_DIR);
}
}
let base = dirs::data_dir().unwrap_or_else(|| PathBuf::from(".local/share"));
base.join(APP_DIR)
}
#[must_use]
pub fn linkmarks_config_dir() -> PathBuf {
if let Some(xdg) = std::env::var_os("XDG_CONFIG_HOME") {
let trimmed = xdg.to_string_lossy().trim().to_string();
if !trimmed.is_empty() {
return PathBuf::from(trimmed).join(APP_DIR);
}
}
let base = dirs::config_dir().unwrap_or_else(|| PathBuf::from(".config"));
base.join(APP_DIR)
}
#[must_use]
pub fn linkmarks_store_path() -> PathBuf {
linkmarks_data_dir().join(STORE_FILENAME)
}
#[must_use]
pub fn linkmarks_config_path() -> PathBuf {
linkmarks_config_dir().join(CONFIG_FILENAME)
}
pub fn ensure_data_dir() -> std::io::Result<PathBuf> {
let dir = linkmarks_data_dir();
std::fs::create_dir_all(&dir)?;
Ok(dir)
}
pub fn ensure_config_dir() -> std::io::Result<PathBuf> {
let dir = linkmarks_config_dir();
std::fs::create_dir_all(&dir)?;
Ok(dir)
}
#[must_use]
pub fn default_data_dir_display() -> String {
linkmarks_data_dir().to_string_lossy().into_owned()
}
#[must_use]
pub fn default_config_dir_display() -> String {
linkmarks_config_dir().to_string_lossy().into_owned()
}
#[must_use]
pub fn is_inside_data_dir(path: &Path) -> bool {
path.starts_with(linkmarks_data_dir())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn store_path_lives_under_data_dir() {
let store = linkmarks_store_path();
assert!(store.ends_with(STORE_FILENAME));
assert!(is_inside_data_dir(&store));
}
#[test]
fn config_path_lives_under_config_dir() {
let cfg = linkmarks_config_path();
assert!(cfg.ends_with(CONFIG_FILENAME));
assert!(cfg.starts_with(linkmarks_config_dir()));
}
#[test]
fn data_and_config_dirs_are_distinct() {
assert_ne!(linkmarks_data_dir(), linkmarks_config_dir());
}
#[test]
fn ensure_data_dir_is_idempotent() {
let dir = ensure_data_dir().expect("create data dir");
assert!(dir.is_dir());
let again = ensure_data_dir().expect("idempotent create");
assert_eq!(dir, again);
}
#[test]
fn ensure_config_dir_is_idempotent() {
let dir = ensure_config_dir().expect("create config dir");
assert!(dir.is_dir());
let again = ensure_config_dir().expect("idempotent create");
assert_eq!(dir, again);
}
}