use std::collections::HashSet;
use std::fmt::Display;
use std::fs;
use std::path::PathBuf;
use anyhow::anyhow;
use anyhow::Result;
use directories::ProjectDirs;
use figment::providers::{Env, Format, Serialized, Toml};
use figment::Figment;
use lazy_static::lazy_static;
use owo_colors::OwoColorize;
use serde::{Deserialize, Serialize};
lazy_static! {
pub static ref DIRS: ProjectDirs =
ProjectDirs::from("me", "greenboi", "Papa").expect("Unable to find base dirs");
pub static ref CONFIG: Config = {
let path = DIRS.config_dir().join("config.toml");
let mut cfg: Config = Figment::from(Serialized::defaults(Config::default()))
.merge(Toml::file(&path))
.merge(Env::prefixed("PAPA_"))
.extract()
.expect("Error reading configuration");
cfg.config_path = Some(path);
cfg
};
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Config {
#[serde(skip)]
pub config_path: Option<PathBuf>,
game_dir: Option<PathBuf>,
install_dir: Option<PathBuf>,
#[serde(default = "default_profile")]
current_profile: String,
#[serde(default = "default_ignore_list")]
ignore: HashSet<String>,
#[serde(default)]
install_type: InstallType,
is_server: bool,
}
impl Config {
pub fn install_dir(&self) -> Result<PathBuf> {
Ok(if let Some(dir) = &self.install_dir {
dir.clone()
} else if let Some(dir) = &self.game_dir {
dir.join(&self.current_profile).join("packages")
} else {
println!(
"Please run '{}' or set '{}' in the config",
"papa ns init".bright_cyan(),
"install_dir".bright_cyan()
);
return Err(anyhow!("Unintialized config"));
})
}
pub fn is_server(&self) -> bool {
self.is_server
}
pub fn set_install_dir(&mut self, install_dir: impl Into<PathBuf>) {
self.install_dir = install_dir.into().into();
}
pub fn set_is_server(&mut self, is_server: bool) {
self.is_server = is_server;
}
pub fn set_game_dir(&mut self, game_dir: impl Into<Option<PathBuf>>) {
self.game_dir = game_dir.into();
}
pub fn game_dir(&self) -> Option<&PathBuf> {
self.game_dir.as_ref()
}
pub fn install_type(&self) -> &InstallType {
&self.install_type
}
pub fn set_install_type(&mut self, install_type: InstallType) {
self.install_type = install_type;
}
pub fn current_profile(&self) -> &str {
self.current_profile.as_ref()
}
pub fn set_current_profile(&mut self, current_profile: impl Into<String>) {
self.current_profile = current_profile.into();
}
pub fn is_ignored(&self, val: &str) -> bool {
self.ignore.contains(val)
}
pub fn add_ignored(&mut self, val: impl Into<String>) -> bool {
self.ignore.insert(val.into())
}
pub fn remove_ignored(&mut self, val: impl AsRef<str>) -> bool {
self.ignore.remove(val.as_ref())
}
}
impl Default for Config {
fn default() -> Self {
Self {
game_dir: None,
install_dir: None,
is_server: false,
config_path: None,
current_profile: default_profile(),
ignore: default_ignore_list(),
install_type: InstallType::Other,
}
}
}
pub fn default_profile() -> String {
"R2Northstar".into()
}
pub fn default_ignore_list() -> HashSet<String> {
let list = include_str!("./ignore_list.csv").trim();
list.split('\n').map(String::from).collect()
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum InstallType {
Steam,
Origin,
EA,
#[default]
Other,
}
impl Display for InstallType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
pub fn write_config(cfg: &Config) -> Result<()> {
let cereal = toml::to_string_pretty(cfg)?;
fs::create_dir_all(DIRS.config_dir())?;
fs::write(DIRS.config_dir().join("config.toml"), cereal)?;
Ok(())
}