1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
//! Basic config API
//!
//! Contains a special trait that allows a type to have an associated config file
use serde::{Deserialize, Serialize};
use std::{
fs,
io::Result as IoResult
};
use ron::ser::PrettyConfig;
/// The type of config file we are using\
#[derive(Clone, Copy)]
pub enum ConfigType {
Toml,
Ron
}
//TODO: use PathBuf instead of String for file paths
//TODO: could I have this trait defined by a macro?
/// The `Config` trait allows a type to have an associated config file
///
/// Requires the `Default`, `Serialize`, and `Deserialize` traits (can be derived) to be implemented
pub trait Config: Serialize + Default + for <'de> Deserialize<'de> {
/// This constant must be set to the file name of the config file
const FILE_NAME: &'static str;
/// This constant is the type of config
///
/// Defaults to TOML
const TYPE: ConfigType = ConfigType::Toml;
/// Returns the save directory of the config file.
///
/// This method must be defined
fn get_save_dir() -> String;
/// Returns the full path of the config file
fn get_full_path() -> String { format!("{}/{}", Self::get_save_dir(), Self::FILE_NAME) }
/// Load from the config file
///
/// Returns the defaults if the file does not exist or is corrupted
///
/// Panics if the config directory cannot be created
#[track_caller]
fn load() -> Self {
let mut config = Self::default();
let file_str = match fs::read_to_string(Self::get_full_path()) {
Ok(file) => file,
Err(_) => {
config.save().expect("Unable to load config!");
return config;
}
};
match Self::TYPE {
ConfigType::Toml => {
match toml::from_str(&file_str) {
Ok(t) => config = t,
Err(_) => return Self::default(),
}
}
ConfigType::Ron => {
match ron::from_str(&file_str) {
Ok(r) => config = r,
Err(_) => return Self::default()
}
}
}
config
}
/// Save to the config file
///
/// It is recommended to call this in every method that has &mut self as an argument!
#[track_caller]
fn save(&self) -> IoResult<()> {
let save_str = match Self::TYPE {
ConfigType::Toml => toml::to_string_pretty(&self).unwrap(),
ConfigType::Ron => {
let cfg = PrettyConfig::default();
ron::ser::to_string_pretty(&self, cfg).unwrap()
}
};
fs::create_dir_all(Self::get_save_dir())?;
fs::write(Self::get_full_path(), save_str)
}
}