use crate::catalog;
use crate::error::FilesystemError;
use glob::MatchOptions;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use std::fs::create_dir_all;
use std::path::PathBuf;
mod addons;
mod wow;
use crate::fs::PersistentData;
pub use crate::config::addons::Addons;
pub use crate::config::wow::{Flavor, Wow};
#[derive(Deserialize, Serialize, Debug, PartialEq, Default, Clone)]
pub struct Config {
#[serde(default)]
pub wow: Wow,
#[serde(default)]
pub addons: Addons,
pub theme: Option<String>,
#[serde(default)]
pub column_config: ColumnConfig,
pub window_size: Option<(u32, u32)>,
pub scale: Option<f64>,
pub backup_directory: Option<PathBuf>,
#[serde(default)]
pub backup_addons: bool,
#[serde(default)]
pub backup_wtf: bool,
#[serde(default)]
pub hide_ignored_addons: bool,
#[serde(default)]
pub self_update_channel: SelfUpdateChannel,
#[serde(default)]
pub weak_auras_account: HashMap<Flavor, String>,
#[serde(default = "default_true")]
pub alternating_row_colors: bool,
#[serde(default)]
pub language: Language,
#[serde(default)]
pub catalog_source: Option<catalog::Source>,
#[serde(default)]
pub auto_update: bool,
}
impl Config {
pub fn get_flavor_directory_for_flavor(&self, flavor: &Flavor, path: &PathBuf) -> PathBuf {
path.join(&flavor.folder_name())
}
pub fn get_root_directory_for_flavor(&self, flavor: &Flavor) -> Option<PathBuf> {
if let Some(flavor_dir) = self.wow.directories.get(flavor) {
Some(flavor_dir.parent().unwrap().to_path_buf())
} else {
None
}
}
pub fn get_addon_directory_for_flavor(&self, flavor: &Flavor) -> Option<PathBuf> {
let dir = self.wow.directories.get(flavor);
match dir {
Some(dir) => {
let mut addon_dir = dir.join("Interface/AddOns");
if !addon_dir.exists() {
let options = MatchOptions {
case_sensitive: false,
..Default::default()
};
let pattern = format!("{}/?nterface/?ddons", dir.display());
for entry in glob::glob_with(&pattern, options).unwrap() {
if let Ok(path) = entry {
addon_dir = path;
}
}
}
if dir.exists() && !addon_dir.exists() {
let _ = create_dir_all(&addon_dir);
}
Some(addon_dir)
}
None => None,
}
}
pub fn get_download_directory_for_flavor(&self, flavor: Flavor) -> Option<PathBuf> {
self.wow.directories.get(&flavor).cloned()
}
pub fn get_wtf_directory_for_flavor(&self, flavor: &Flavor) -> Option<PathBuf> {
let dir = self.wow.directories.get(flavor);
match dir {
Some(dir) => {
let mut addon_dir = dir.join("WTF");
if !addon_dir.exists() {
let options = MatchOptions {
case_sensitive: false,
..Default::default()
};
let pattern = format!("{}/?tf", dir.display());
for entry in glob::glob_with(&pattern, options).unwrap() {
if let Ok(path) = entry {
addon_dir = path;
}
}
}
Some(addon_dir)
}
None => None,
}
}
}
impl PersistentData for Config {
fn relative_path() -> PathBuf {
PathBuf::from("ajour.yml")
}
}
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub enum ColumnConfig {
V1 {
local_version_width: u16,
remote_version_width: u16,
status_width: u16,
},
V2 {
columns: Vec<ColumnConfigV2>,
},
V3 {
my_addons_columns: Vec<ColumnConfigV2>,
catalog_columns: Vec<ColumnConfigV2>,
#[serde(default)]
aura_columns: Vec<ColumnConfigV2>,
},
}
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub struct ColumnConfigV2 {
pub key: String,
pub width: Option<u16>,
pub hidden: bool,
}
impl Default for ColumnConfig {
fn default() -> Self {
ColumnConfig::V1 {
local_version_width: 150,
remote_version_width: 150,
status_width: 85,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SelfUpdateChannel {
Stable,
Beta,
}
impl SelfUpdateChannel {
pub const fn all() -> [Self; 2] {
[SelfUpdateChannel::Stable, SelfUpdateChannel::Beta]
}
}
impl Default for SelfUpdateChannel {
fn default() -> Self {
SelfUpdateChannel::Stable
}
}
impl Display for SelfUpdateChannel {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = match self {
SelfUpdateChannel::Stable => "Stable",
SelfUpdateChannel::Beta => "Beta",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash, PartialOrd, Ord)]
pub enum Language {
Czech,
Norwegian,
English,
Danish,
German,
French,
Hungarian,
Portuguese,
Russian,
Slovak,
Swedish,
Spanish,
Turkish,
Ukrainian,
}
impl std::fmt::Display for Language {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Language::Czech => "Čeština",
Language::Danish => "Dansk",
Language::English => "English",
Language::French => "Français",
Language::German => "Deutsch",
Language::Hungarian => "Magyar",
Language::Norwegian => "Norsk Bokmål",
Language::Portuguese => "Português",
Language::Russian => "Pусский",
Language::Slovak => "Slovenčina",
Language::Spanish => "Español",
Language::Swedish => "Svenska",
Language::Turkish => "Türkçe",
Language::Ukrainian => "Yкраїнська",
}
)
}
}
impl Language {
pub const ALL: [Language; 14] = [
Language::Czech,
Language::Danish,
Language::German,
Language::English,
Language::Spanish,
Language::French,
Language::Hungarian,
Language::Norwegian,
Language::Portuguese,
Language::Russian,
Language::Slovak,
Language::Swedish,
Language::Turkish,
Language::Ukrainian,
];
pub const fn language_code(self) -> &'static str {
match self {
Language::Czech => "cs_CZ",
Language::English => "en_US",
Language::Danish => "da_DK",
Language::German => "de_DE",
Language::French => "fr_FR",
Language::Russian => "ru_RU",
Language::Swedish => "se_SE",
Language::Spanish => "es_ES",
Language::Hungarian => "hu_HU",
Language::Norwegian => "nb_NO",
Language::Slovak => "sk_SK",
Language::Turkish => "tr_TR",
Language::Portuguese => "pt_PT",
Language::Ukrainian => "uk_UA",
}
}
}
impl Default for Language {
fn default() -> Language {
Language::English
}
}
pub async fn load_config() -> Result<Config, FilesystemError> {
log::debug!("loading config");
Ok(Config::load_or_default()?)
}
const fn default_true() -> bool {
true
}