use serde::{Deserialize, Serialize};
macro_rules! create_options {
(
$(#[$attr:meta])*
pub struct Options {
$(
$(#[$field_attr:meta])*
pub $name:ident: $ty:ty,
)+
}
) => {
$(#[$attr])*
pub struct Options {
$(
$(#[$field_attr])*
pub $name: $ty,
)+
}
impl Options {
pub fn update(&mut self, incomplete: OptionsIncomplete) {
$(
if let Some(v) = incomplete.$name {
self.$name = v;
}
)+
}
pub fn update_camel(&mut self, incomplete: OptionsIncompleteCamel) {
$(
if let Some(v) = incomplete.$name {
self.$name = v;
}
)+
}
pub fn update_from_str<S: AsRef<str>, I: Iterator<Item = (S, S)>>(
&mut self,
values: I,
) -> Result<(), OptionParseError> {
for (key, val) in values {
$(
if key.as_ref() == stringify!($name) {
self.$name =
val.as_ref()
.parse()
.map_err(|error| OptionParseError::InvalidValue {
key: key.as_ref().into(),
error: Box::new(error),
})?;
continue;
}
)+
return Err(OptionParseError::InvalidOption(key.as_ref().into()));
}
Ok(())
}
}
$(#[$attr])*
#[derive(Default)]
pub struct OptionsIncomplete {
$(
$(#[$field_attr])*
pub $name: Option<$ty>,
)+
}
impl OptionsIncomplete {
pub fn from_options(opts: Options) -> Self {
let mut o = Self::default();
$(
o.$name = Some(opts.$name);
)+
o
}
}
$(#[$attr])*
#[derive(Default)]
#[serde(rename_all = "camelCase")]
pub struct OptionsIncompleteCamel {
$(
$(#[$field_attr])*
pub $name: Option<$ty>,
)+
}
impl OptionsIncompleteCamel {
pub fn from_options(opts: Options) -> Self {
let mut o = Self::default();
$(
o.$name = Some(opts.$name);
)+
o
}
}
};
}
create_options!(
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Options {
pub indent_string: String,
pub trailing_comma: bool,
pub trailing_newline: bool,
pub format_key: bool,
}
);
#[derive(Debug)]
pub enum OptionParseError {
InvalidOption(String),
InvalidValue {
key: String,
error: Box<dyn std::error::Error + Send + Sync>,
},
}
impl core::fmt::Display for OptionParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"invalid formatting option: {}",
match self {
OptionParseError::InvalidOption(k) => {
format!(r#"invalid option "{}""#, k)
}
OptionParseError::InvalidValue { key, error } => {
format!(r#"invalid value for option "{}": {}"#, key, error)
}
}
)
}
}
impl std::error::Error for OptionParseError {}
impl Default for Options {
fn default() -> Self {
Options {
indent_string: " ".into(),
trailing_comma: false,
trailing_newline: false,
format_key: false,
}
}
}