use std::collections::{HashMap, HashSet};
use std::io::{self, Error as IoError, ErrorKind as Kind};
use serde::de;
use serde::Deserialize;
use crate::de::{Deserializer, SECTION_CTX};
use crate::error::Result;
use crate::parser::Watcher;
use crate::tokenizer::Mode;
pub fn from_str<T>(s: &str) -> Result<T>
where
T: for<'de> Deserialize<'de>,
{
Builder::new().from_str(s)
}
pub fn from_file<T>(name: impl Into<String>) -> io::Result<T>
where
T: for<'de> Deserialize<'de>,
{
Builder::new().from_file(name)
}
pub struct Builder {
mode: Mode,
aliases: HashMap<String, String>,
ignored: HashSet<String>,
watcher: Option<Watcher>,
}
impl Builder {
pub fn new() -> Builder {
Builder {
mode: Mode::Semicolon,
aliases: HashMap::new(),
ignored: HashSet::new(),
watcher: None,
}
}
pub fn mode(mut self, mode: Mode) -> Builder {
self.mode = mode;
self
}
pub fn alias<T>(mut self, alias: &str, section_name: &str) -> Builder {
let type_name = std::any::type_name::<T>().split(":").last().unwrap();
let type_dot_alias = format!("{}.{}", type_name, alias);
self.aliases
.insert(type_dot_alias, section_name.to_string());
self
}
pub fn ignore<T>(mut self, key: &str) -> Builder {
let type_name = std::any::type_name::<T>().split(":").last().unwrap();
let type_dot_alias = format!("{}.{}", type_name, key);
self.ignored.insert(type_dot_alias);
self
}
pub fn watcher(mut self, watcher: &Watcher) -> Builder {
self.watcher = Some(watcher.clone());
self
}
pub fn from_str<T>(self, text: &str) -> Result<T>
where
T: for<'de> Deserialize<'de>,
{
let mut deserializer =
Deserializer::from_str(text, self.mode, self.aliases, self.ignored, self.watcher);
T::deserialize(&mut deserializer)
}
pub fn from_file<T>(self, name: impl Into<String>) -> io::Result<T>
where
T: for<'de> Deserialize<'de>,
{
let mut deserializer =
Deserializer::from_file(name, self.mode, self.aliases, self.ignored, self.watcher)?;
T::deserialize(&mut deserializer)
.map_err(|e| IoError::new(Kind::Other, e))
}
}
pub trait ParserAccess {
fn value_name(&self) -> String {
SECTION_CTX.with(|ctx| ctx.borrow().subsection_name().to_string())
}
fn parser(&self) -> Parser {
Parser
}
}
impl<'de, T> ParserAccess for T where T: de::Deserializer<'de> {}
pub struct Parser;
impl ParserAccess for Parser {}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
fn test_struct() {
init();
#[derive(Deserialize, PartialEq, Debug)]
struct Main {
test: Test,
}
#[derive(Deserialize, PartialEq, Debug)]
#[serde(rename = "test")]
struct Test {
__label__: String,
int: u32,
seq: Vec<String>,
}
let j = r#"test foo {
int 1;
seq a,"b";
}"#;
let expected = Main {
test: Test {
__label__: "foo".to_owned(),
int: 1,
seq: vec!["a".to_owned(), "b".to_owned()],
},
};
assert_eq!(expected, from_str(j).unwrap());
}
}