Rewritten some parts as macros, updated extension handling
Encoding and decoding of `settings::Settings` now handled in macros to avoid code repetition. I also wish rewrite them both in a combined macro that would either encode or decode the struct depending on how it was called. Replaced some log warnings with errors Now if type extension is not valid, the code will still attempt to decode it, first as JSON data, then as Yaml data. If both fail, then an error is returned. For this, I added the Display trait to the `settings::Settings` struct which allows the FromStr trait, and due to conflicting implementation, I removed the From trait and implemented manually the `from` method with the same signature -- the struct just lacks the trait that comes with it.
This commit is contained in:
parent
d200367ee0
commit
18eb16e777
@ -4,7 +4,7 @@ extern crate serde_yaml;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
use log::{info, warn};
|
use log::{error, info};
|
||||||
|
|
||||||
// mod utils;
|
// mod utils;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
@ -15,6 +15,80 @@ use utils::SettingsType;
|
|||||||
/// use.
|
/// use.
|
||||||
pub const RULESET_CURRENT_VERSION: i32 = 1;
|
pub const RULESET_CURRENT_VERSION: i32 = 1;
|
||||||
|
|
||||||
|
/// Encode a [`Settings`] struct to a filetype, returns a
|
||||||
|
/// `std::result::Result<std::string::String, std::io::Error>`
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `funcrate` - `serde`-compatible crate to use, must implement `to_string`
|
||||||
|
/// * `content` - content to encode, must be `Settings` struct
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// # use lang_evolve_core::settings::*;
|
||||||
|
/// # use lang_evolve_core::encode_settings;
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
/// use std::path::Path;
|
||||||
|
/// let filetype = utils::get_file_type(Path::new("./path/to/file.json"));
|
||||||
|
/// let s = Settings::new();
|
||||||
|
/// let content = match filetype {
|
||||||
|
/// utils::SettingsType::Yaml => encode_settings!(serde_yaml, &s).unwrap(),
|
||||||
|
/// utils::SettingsType::Json => encode_settings!(serde_json, &s).unwrap(),
|
||||||
|
/// _ => panic!("Could not encode settings"),
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`Settings`]: ./settings/struct.Settings.html
|
||||||
|
#[macro_export(local_inner_macros)]
|
||||||
|
macro_rules! encode_settings {
|
||||||
|
($funcrate:ident, $content:expr) => {
|
||||||
|
match $funcrate::to_string($content) {
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not serialize settings: {}", e.to_string());
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::InvalidData,
|
||||||
|
e,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(val) => val,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode a [`Settings`] struct from a `std::std::String`, returns a
|
||||||
|
/// std::result::Result<lang_evolve_core::settings::Settings, std::io::Error>
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `funcrate` - `serde`-compatible crate to use, mus implement `from_string`
|
||||||
|
/// * `content` - `&str` to decode into a [`Settings`]
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// # use lang_evolve_core::decode_settings;
|
||||||
|
/// let str = r#"{"version":"1","categories":[],"rules":[]}"#;
|
||||||
|
/// let settings = decode_settings!(serde_json, str);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`Settings`]: ./settings/struct.Settings.html
|
||||||
|
#[macro_export(local_inner_macros)]
|
||||||
|
macro_rules! decode_settings {
|
||||||
|
($funcrate:ident, $content:expr) => {
|
||||||
|
match $funcrate::from_str($content) {
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not import settings: {}", e.to_string());
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::InvalidInput,
|
||||||
|
e,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(val) => val,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
#[serde(default = "Settings::get_ruleset_version")]
|
#[serde(default = "Settings::get_ruleset_version")]
|
||||||
@ -75,37 +149,27 @@ impl Settings {
|
|||||||
/// let _s_yml = Settings::import(&path_yml).unwrap();
|
/// let _s_yml = Settings::import(&path_yml).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn import(path: &std::path::Path) -> std::io::Result<Self> {
|
pub fn import(path: &std::path::Path) -> std::io::Result<Self> {
|
||||||
use SettingsType::*;
|
use utils::SettingsType::{Json, Yaml};
|
||||||
let display = path.display();
|
let file_type = utils::get_file_type(&path);
|
||||||
let file_type = utils::get_file_type(&path).unwrap();
|
let content = utils::read_file(&path)?;
|
||||||
let content = match utils::read_file(&path) {
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not read file {}: {}", display, e.to_string());
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
Ok(content) => content,
|
|
||||||
};
|
|
||||||
|
|
||||||
let settings: Settings = match file_type {
|
let settings: Settings = match file_type {
|
||||||
Yaml => match serde_yaml::from_str(&content) {
|
Yaml => decode_settings!(serde_yaml, &content),
|
||||||
|
Json => decode_settings!(serde_json, &content),
|
||||||
|
// Attempt to decode anyway
|
||||||
|
_ => match Settings::from_str(&content.as_str()) {
|
||||||
|
Ok(val) => val,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Could not import settings: {}", e.to_string());
|
error!(
|
||||||
|
"Could not decode input {}: {}",
|
||||||
|
content,
|
||||||
|
e.to_string()
|
||||||
|
);
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::InvalidInput,
|
std::io::ErrorKind::InvalidData,
|
||||||
e,
|
e,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(val) => val,
|
|
||||||
},
|
|
||||||
Json => match serde_json::from_str(&content) {
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not import settings: {}", e.to_string());
|
|
||||||
return Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::InvalidInput,
|
|
||||||
e,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(val) => val,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
info!("Successfuly imported {}", path.display());
|
info!("Successfuly imported {}", path.display());
|
||||||
@ -140,28 +204,17 @@ impl Settings {
|
|||||||
/// s.export(&path_yml).unwrap();
|
/// s.export(&path_yml).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn export(&self, path: &std::path::Path) -> std::io::Result<()> {
|
pub fn export(&self, path: &std::path::Path) -> std::io::Result<()> {
|
||||||
let filetype = utils::get_file_type(&path).unwrap();
|
let filetype = utils::get_file_type(&path);
|
||||||
let content = match filetype {
|
let content = match filetype {
|
||||||
SettingsType::Yaml => match serde_yaml::to_string(&self) {
|
SettingsType::Yaml => encode_settings!(serde_yaml, &self),
|
||||||
Err(e) => {
|
SettingsType::Json => encode_settings!(serde_json, &self),
|
||||||
warn!("Could not serialize settings: {}", e.to_string());
|
_ => {
|
||||||
|
error!("Unknown filetype {}", path.to_str().unwrap());
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::InvalidData,
|
std::io::ErrorKind::InvalidData,
|
||||||
e,
|
"Unknown file type",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(val) => val,
|
|
||||||
},
|
|
||||||
SettingsType::Json => match serde_json::to_string(&self) {
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not serialize settings: {}", e.to_string());
|
|
||||||
return Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::InvalidData,
|
|
||||||
e,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(val) => val,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
info!("Successfuly exported settings to {}", path.display());
|
info!("Successfuly exported settings to {}", path.display());
|
||||||
utils::write_file(&path, &content)
|
utils::write_file(&path, &content)
|
||||||
@ -173,6 +226,47 @@ impl Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
impl FromStr for Settings {
|
||||||
|
type Err = serde_yaml::Error;
|
||||||
|
|
||||||
|
/// Decode a litteral string into a `Settings` struct. Works only for
|
||||||
|
/// supported file types described in `SettingsType`. It will try to decode
|
||||||
|
/// the input `s` by any mean known by `SettingsType`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `s` - litteral string to decode into a `Settings` struct
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use std::str::FromStr;
|
||||||
|
/// let s = r#"{"version":"1","categories":[],"rules":[]}"#;
|
||||||
|
/// let settings = lang_evolve_core::settings::Settings::from_str(s).unwrap();
|
||||||
|
/// ```
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match serde_json::from_str::<Settings>(s) {
|
||||||
|
Ok(val) => Ok(val),
|
||||||
|
Err(_) => match serde_yaml::from_str::<Settings>(s) {
|
||||||
|
Ok(val) => Ok(val),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not decode input {}: {}", s, e.to_string());
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Display;
|
||||||
|
impl Display for Settings {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", serde_json::to_string(&self).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq for Settings {
|
impl PartialEq for Settings {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.version == other.version
|
self.version == other.version
|
||||||
@ -181,28 +275,6 @@ impl PartialEq for Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> From<S> for Settings
|
|
||||||
where
|
|
||||||
S: ToString,
|
|
||||||
{
|
|
||||||
/// Import settings from file path described by the argument `source`
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `source` - path to the file from which settings should be imported
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// let s = lang_evolve_core::settings::Settings::from("settings.yml");
|
|
||||||
/// ```
|
|
||||||
fn from(source: S) -> Self {
|
|
||||||
let source = source.to_string();
|
|
||||||
let path = std::path::Path::new(&source);
|
|
||||||
Settings::import(&path).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Settings {}
|
impl Eq for Settings {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user