#![deny(missing_docs)]
#[macro_use]
extern crate err_derive;
#[macro_use]
extern crate pest_derive;
use pest::Parser;
use pest::error::Error;
use pest::iterators::Pair;
pub(crate) mod parser {
#[derive(Parser)]
#[grammar = "bml.pest"]
pub struct BmlParser;
}
use parser::{BmlParser, Rule};
#[derive(Debug, PartialEq, Eq, Clone, Error)]
#[error(display = "Invalid BML\n{}", inner)]
pub struct BmlError {
inner: Error<Rule>,
}
#[cfg(feature = "ordered-multimap")]
use ordered_multimap::ListOrderedMultimap;
use smallstr::SmallString;
use std::convert::TryFrom;
use std::fmt;
type BmlName = SmallString<[u8; 32]>;
type BmlData = SmallString<[u8; 32]>;
#[derive(Debug, Eq, Clone, Copy)]
enum BmlKind {
Root { indent: BmlIndent },
Elem,
Attr { quote: bool },
}
use BmlKind::*;
impl PartialEq for BmlKind {
fn eq(&self, other: &BmlKind) -> bool {
match (self, other) {
(Root { .. }, Root { .. }) => true,
(Elem, Elem) => true,
(Attr { .. }, Attr { .. }) => true,
_ => false,
}
}
}
impl Default for BmlKind {
fn default() -> Self {
Root { indent: BmlIndent::default() }
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct BmlIndent {
string: &'static str,
repeat: usize,
}
impl BmlIndent {
fn next(mut self) -> Self {
self.repeat += 1;
self
}
}
impl Default for BmlIndent {
fn default() -> Self {
Self { string: " ".into(), repeat: 0 }
}
}
impl fmt::Display for BmlIndent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.string.repeat(self.repeat))
}
}
#[derive(Debug, Eq, Clone, Default)]
pub struct BmlNode {
kind: BmlKind,
data: BmlData,
#[cfg(feature = "ordered-multimap")]
node: ListOrderedMultimap<BmlName, BmlNode>,
#[cfg(not(feature = "ordered-multimap"))]
node: Vec<(BmlName, BmlNode)>,
}
impl BmlNode {
pub fn value(&self) -> &str {
&self.data[..self.data.len() - 1]
}
pub fn lines(&self) -> impl DoubleEndedIterator<Item = &str> {
self.data.lines()
}
pub fn nodes(&self)
-> impl DoubleEndedIterator<Item = (&str, &BmlNode)> + ExactSizeIterator {
self.node.iter().map(|(name, node)| (name.as_str(), node))
}
#[cfg(feature = "ordered-multimap")]
pub fn named(&self, name: &str)
-> impl DoubleEndedIterator<Item = &BmlNode> + ExactSizeIterator {
self.node.get_all(name)
}
#[cfg(not(feature = "ordered-multimap"))]
pub fn named(&self, name: &str)
-> impl DoubleEndedIterator<Item = &BmlNode> {
let name = BmlName::from(name);
self.node.iter().filter_map(move |(key, value)|
if key == &name { Some(value) } else { None })
}
pub fn set_indent(&mut self, string: &'static str, repeat: usize) {
match self.kind {
Root { ref mut indent } => *indent = BmlIndent { string, repeat },
_ => panic!("BML indent can be set for root node only"),
}
}
fn root() -> Self {
Self { kind: Root { indent: BmlIndent::default() }, ..Self::default() }
}
fn elem() -> Self {
Self { kind: Elem, ..Self::default() }
}
fn attr() -> Self {
Self { kind: Attr { quote: true }, ..Self::default() }
}
fn append(&mut self, (name, node): (BmlName, BmlNode)) {
#[cfg(feature = "ordered-multimap")]
self.node.append(name, node);
#[cfg(not(feature = "ordered-multimap"))]
self.node.push((name, node));
}
fn serialize(&self, f: &mut fmt::Formatter,
name: &str, indent: BmlIndent,
) -> fmt::Result {
match self.kind {
Root { indent } => {
let mut nodes = self.nodes().peekable();
while let Some((name, node)) = nodes.next() {
node.serialize(f, name, indent)?;
if nodes.peek().is_some() {
writeln!(f)?;
}
}
},
Elem => {
write!(f, "{}{}", indent, name)?;
let indent = indent.next();
let mut attrs = 0;
for (name, attr) in self.nodes().take_while(|(_name, node)|
if let Attr { .. } = node.kind
{ true} else { false }
) {
attrs += 1;
attr.serialize(f, name, indent)?;
}
let mut lines = self.lines();
let line0 = lines.next();
let line1 = lines.next();
if attrs == 0 && line0.is_some() && line1.is_none() {
writeln!(f, ": {}", line0.unwrap())?;
} else {
writeln!(f)?;
for line in self.lines() {
writeln!(f, "{}:{}", indent, line)?;
}
}
for (name, elem) in self.nodes().skip(attrs) {
elem.serialize(f, name, indent)?;
}
},
Attr { quote } => {
write!(f, " {}", name)?;
if let Some(line) = self.lines().next() {
if quote {
write!(f, "=\"{}\"", line)?;
} else {
write!(f, "={}", line)?;
}
}
},
}
Ok(())
}
}
impl PartialEq for BmlNode {
fn eq(&self, other: &BmlNode) -> bool {
self.data == other.data && self.node == other.node
}
}
impl TryFrom<&str> for BmlNode {
type Error = BmlError;
fn try_from(bml: &str) -> Result<Self, Self::Error> {
fn parse_node(pair: Pair<Rule>) -> (BmlName, BmlNode) {
let mut name = BmlName::new();
let mut node = BmlNode::elem();
for pair in pair.into_inner() {
match pair.as_rule() {
Rule::name => name = pair.as_str().into(),
Rule::data => {
node.data.push_str(pair.into_inner().as_str());
node.data.push('\n');
},
Rule::attr => {
let mut name = BmlName::new();
let mut attr = BmlNode::attr();
for pair in pair.into_inner() {
match pair.as_rule() {
Rule::name => name = pair.as_str().into(),
Rule::data => for data in pair.into_inner() {
if data.as_rule() == Rule::space_data_inner
{ attr.kind = Attr { quote: false } };
attr.data.push_str(data.as_str());
attr.data.push('\n');
},
_ => unreachable!(),
}
}
node.append((name, attr));
},
Rule::node => node.append(parse_node(pair)),
_ => unreachable!(),
}
}
(name, node)
}
let mut root = BmlNode::root();
for pair in BmlParser::parse(Rule::root, &bml)
.map_err(|inner| BmlError { inner })?
{
match pair.as_rule() {
Rule::node => root.append(parse_node(pair)),
Rule::EOI => (),
_ => unreachable!(),
}
}
Ok(root)
}
}
impl fmt::Display for BmlNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.serialize(f, "", BmlIndent::default())
}
}