use super::*;
#[derive(Default)]
pub struct Loader
{
by_name: HashMap<String, grammar::Block>,
refs: HashMap<String, grammar::Block>,
roots: Vec<grammar::Block>,
to_include: HashSet<PathBuf>
}
impl Loader
{
fn add_elem(&mut self, elem: grammar::DocumentElem) -> StdResult
{
match elem
{
grammar::DocumentElem::Root(block) =>
{
self.record_names(&block)?;
self.roots.push(block);
}
grammar::DocumentElem::Ref(block) =>
{
self.record_names(&block)?;
match &block.name
{
Some(name) =>
{ self.refs.insert(name.clone(), block); }
None =>
{ return Err(From::from("Ref blocks must be named")); }
}
}
grammar::DocumentElem::Include(path) =>
{
self.to_include.insert(path);
}
}
Ok(())
}
fn record_names(&mut self, block: &grammar::Block) -> StdResult
{
if let Some(name) = &block.name
{
if self.by_name.contains_key(name)
{
return Err(From::from(
format!("Multiple blocks named {}", name)
));
}
self.by_name.insert(name.to_owned(), block.clone());
}
for (_, maybe_child) in block.children.iter()
{
if let grammar::Child::Defined(child) = maybe_child
{
self.record_names(&child)?;
}
}
Ok(())
}
pub fn add_document(&mut self, s: &str, path: impl AsRef<Path>)
-> StdResult
{
let doc = grammar::document(s, path.as_ref())?;
for elem in doc.elems
{
self.add_elem(elem)?;
}
Ok(())
}
pub fn children<'a>(&'a self, block: &'a grammar::Block)
-> StdResult<Vec<grammar::Block>>
{
self.children_with_maybe_role(block, None)
}
pub fn children_with_role<'a>(
&'a self,
block: &'a grammar::Block,
role: &str
)
-> StdResult<Vec<grammar::Block>>
{
self.children_with_maybe_role(block, Some(role))
}
fn children_with_maybe_role<'a>(
&'a self,
block: &'a grammar::Block,
role: Option<&str>
)
-> StdResult<Vec<grammar::Block>>
{
block.children
.iter()
.filter(|(it_role, _)| it_role.as_ref().map(String::as_str) == role)
.map(|(_, child)| match child
{
grammar::Child::Defined(block) => Ok(block.clone()),
grammar::Child::Ref(name) =>
self.refs.get(name)
.cloned()
.ok_or_else(|| StdError::from(
format!("No block named '{}'", name)
))
}
)
.collect()
}
pub fn roots<'a>(&'a self) -> impl Iterator<Item=grammar::Block> + 'a
{
self.roots.iter().cloned()
}
pub fn to_include(&self) -> &HashSet<PathBuf> { &self.to_include }
}
#[test]
fn test_loader()
{
use serde::Deserialize;
let mut loader = Loader::default();
loader.add_document(r#"
: server {
addr = "0.0.0.0:8080",
&service_1,
&service_2
}
&service_1: service_kind_1 {
key = 1
}
"#, "/").unwrap();
loader.add_document(r#"
&service_2: service_kind_2 {
key = 2
}
"#, "/").unwrap();
#[derive(PartialEq, serde::Deserialize, Debug)]
struct Server { addr: String }
#[derive(PartialEq, serde::Deserialize, Debug)]
struct Service { key: i32 }
let roots: Vec<_> = loader.roots().collect();
assert_eq!(roots.len(), 1);
assert_eq!(
Server::deserialize(&*roots[0]).unwrap(),
Server { addr: "0.0.0.0:8080".to_owned() }
);
let services: Vec<_> = loader.children(&roots[0]).unwrap();
assert_eq!(services.len(), 2);
assert_eq!(
Service::deserialize(&*services[0]).unwrap(),
Service { key: 1 }
);
assert_eq!(services[0].kind, "service_kind_1");
assert_eq!(&services[0].name.clone().unwrap(), "service_1");
assert_eq!(
Service::deserialize(&*services[1]).unwrap(),
Service { key: 2 }
);
assert_eq!(services[1].kind, "service_kind_2");
assert_eq!(services[1].name.clone().unwrap(), "service_2");
}