use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ConfigScope {
Preferences,
UserGlobal,
ProjectLocal,
LocalData,
PersistentData,
Runtime,
}
impl ConfigScope {
pub fn name(self) -> &'static str {
match self {
ConfigScope::Preferences => "Preferences",
ConfigScope::UserGlobal => "UserGlobal",
ConfigScope::ProjectLocal => "ProjectLocal",
ConfigScope::LocalData => "LocalData",
ConfigScope::PersistentData => "PersistentData",
ConfigScope::Runtime => "Runtime",
}
}
pub fn is_file_based(self) -> bool {
matches!(
self,
ConfigScope::Preferences
| ConfigScope::UserGlobal
| ConfigScope::ProjectLocal
| ConfigScope::LocalData
| ConfigScope::PersistentData
)
}
}
pub fn find_config_in(dir: &Path) -> Option<PathBuf> {
for ext in &["toml", "yaml", "yml", "json", "hjson", "ron"] {
let path = dir.join(format!("settings.{}", ext));
if path.exists() && path.is_file() {
return Some(path);
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_scope_names() {
assert_eq!(ConfigScope::Preferences.name(), "Preferences");
assert_eq!(ConfigScope::UserGlobal.name(), "UserGlobal");
assert_eq!(ConfigScope::ProjectLocal.name(), "ProjectLocal");
assert_eq!(ConfigScope::LocalData.name(), "LocalData");
assert_eq!(ConfigScope::PersistentData.name(), "PersistentData");
assert_eq!(ConfigScope::Runtime.name(), "Runtime");
}
#[test]
fn test_config_scope_is_file_based() {
assert!(ConfigScope::Preferences.is_file_based());
assert!(ConfigScope::UserGlobal.is_file_based());
assert!(ConfigScope::ProjectLocal.is_file_based());
assert!(ConfigScope::LocalData.is_file_based());
assert!(ConfigScope::PersistentData.is_file_based());
assert!(!ConfigScope::Runtime.is_file_based());
}
#[test]
fn test_config_scope_equality() {
let scope1 = ConfigScope::Preferences;
let scope2 = ConfigScope::Preferences;
let scope3 = ConfigScope::UserGlobal;
assert_eq!(scope1, scope2);
assert_ne!(scope1, scope3);
}
#[test]
fn test_config_scope_in_collections() {
use std::collections::{HashMap, HashSet};
let mut scope_map: HashMap<ConfigScope, &str> = HashMap::new();
scope_map.insert(ConfigScope::Preferences, "user_prefs");
scope_map.insert(ConfigScope::UserGlobal, "user_config");
assert_eq!(scope_map.get(&ConfigScope::Preferences), Some(&"user_prefs"));
let mut scope_set: HashSet<ConfigScope> = HashSet::new();
scope_set.insert(ConfigScope::ProjectLocal);
scope_set.insert(ConfigScope::LocalData);
assert!(scope_set.contains(&ConfigScope::ProjectLocal));
assert!(!scope_set.contains(&ConfigScope::Preferences));
}
#[test]
fn test_find_config_in_empty_directory() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, None);
}
#[test]
fn test_find_config_in_toml() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let toml_path = temp_dir.path().join("settings.toml");
fs::write(&toml_path, "key = \"value\"").unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, Some(toml_path));
}
#[test]
fn test_find_config_in_yaml() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let yaml_path = temp_dir.path().join("settings.yaml");
fs::write(&yaml_path, "key: value").unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, Some(yaml_path));
}
#[test]
fn test_find_config_in_json() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let json_path = temp_dir.path().join("settings.json");
fs::write(&json_path, r#"{"key": "value"}"#).unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, Some(json_path));
}
#[test]
fn test_find_config_in_prefers_toml_over_yaml() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let toml_path = temp_dir.path().join("settings.toml");
let yaml_path = temp_dir.path().join("settings.yaml");
fs::write(&toml_path, "key = \"toml\"").unwrap();
fs::write(&yaml_path, "key: yaml").unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, Some(toml_path));
}
#[test]
fn test_find_config_in_prefers_yaml_over_json() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let yaml_path = temp_dir.path().join("settings.yaml");
let json_path = temp_dir.path().join("settings.json");
fs::write(&yaml_path, "key: yaml").unwrap();
fs::write(&json_path, r#"{"key": "json"}"#).unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, Some(yaml_path));
}
#[test]
fn test_find_config_in_all_extensions() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let toml_path = temp_dir.path().join("settings.toml");
let yaml_path = temp_dir.path().join("settings.yaml");
let yml_path = temp_dir.path().join("settings.yml");
let json_path = temp_dir.path().join("settings.json");
let hjson_path = temp_dir.path().join("settings.hjson");
let ron_path = temp_dir.path().join("settings.ron");
fs::write(&toml_path, "").unwrap();
fs::write(&yaml_path, "").unwrap();
fs::write(&yml_path, "").unwrap();
fs::write(&json_path, "").unwrap();
fs::write(&hjson_path, "").unwrap();
fs::write(&ron_path, "").unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, Some(toml_path));
}
#[test]
fn test_find_config_in_with_yml_short_form() {
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let yml_path = temp_dir.path().join("settings.yml");
fs::write(&yml_path, "key: value").unwrap();
let result = find_config_in(temp_dir.path());
assert_eq!(result, Some(yml_path));
}
#[test]
fn test_find_config_in_nonexistent_directory() {
let nonexistent = PathBuf::from("/tmp/nonexistent_config_dir_12345");
let result = find_config_in(&nonexistent);
assert_eq!(result, None);
}
}