use crate::{error, Error, String, Vec, vec, ArcStr, HashMap};
use super::node::{Mutator, MutatorIndex, OptionalMutatorIndex};
use super::visual::{Ratio, Pixels, SignedPixels};
use core::{ops::Deref, str::from_utf8 as str_from_utf8, mem::ManuallyDrop};
use xmlparser::{Tokenizer, Token, StrSpan};
use oakwood::{NoCookie, index, tree};
index!(LineNumber, OptionalLineNumber);
index!(FileIndex, OptionalFileIndex);
tree!(XmlNodeTree, XmlNode, XmlNodeKey, XmlNodeIndex, OptionalXmlNodeIndex, NoCookie);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct XmlTagParameters {
pub tag_name: ArcStr,
pub attr_set: &'static [(&'static str, AttributeValueType, Option<ManuallyDrop<ArcStr>>)],
pub accepts_children: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct XmlNode {
pub attributes: AttributeValueVec,
pub factory: OptionalMutatorIndex,
pub file: OptionalFileIndex,
pub line: OptionalLineNumber,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[repr(transparent)]
pub struct AttributeValueVec(Vec<AttributeValue>);
pub fn parse_xml_tree(
mutators_params: HashMap<str, (&XmlTagParameters, MutatorIndex)>,
ordered: &[Mutator],
tree: &mut XmlNodeTree,
xml_bytes: &[u8],
) -> Result<XmlNodeKey, Error> {
use Token::*;
let xml = str_from_utf8(xml_bytes).map_err(|e| error!("xml_bytes: {:?}", e))?;
let line = |span: StrSpan| xml[..span.start()].lines().count();
let unexpected = |thing, as_str, span| error!("Unexpected {}: {:?} (line {})", thing, as_str, line(span));
let unknown = |thing, as_str, span| error!("Unknown {}: {:?} (line {})", thing, as_str, line(span));
let mut current = tree.create();
let mut xml_params = mutators_params.get("import").unwrap().0;
for token in Tokenizer::from(xml) {
let token = token.map_err(|e| error!("XML token error: {:?}", e))?;
if let ElementStart { prefix, local, span } = token {
let prefix = prefix.as_str();
let local = local.as_str();
if prefix != "" {
return Err(unexpected("prefix", prefix, span));
}
let (new_xml_params, index) = match mutators_params.get(local) {
Some(tuple) => tuple,
None => return Err(unknown("XML tag", local, span)),
};
let new_node = tree.create();
tree[new_node].factory = Some(*index).into();
tree[new_node].line = Some(line(span).into()).into();
tree[new_node].attributes = AttributeValueVec::new(new_xml_params);
tree.append_children(new_node, current);
current = new_node;
xml_params = new_xml_params;
}
else if let Attribute { prefix, local, value, span } = token {
let value = value.as_str();
let local = local.as_str();
let index = match xml_params.attr_set.iter().position(|attr| attr.0 == local) {
Some(index) => index,
None => return Err(unknown("attribute", local, span)),
};
let value_type = xml_params.attr_set[index].1;
let value_rc = String::from(value);
tree[current].attributes.0[index] = match prefix.as_str() {
"" => AttributeValue::parse(&value_rc.into(), value_type)?,
prefix => AttributeValue::StateLookup {
namespace: String::from(prefix).into(),
path: value_rc.into(),
value_type,
},
};
}
else if let ElementEnd { end, span } = token {
let current_tag = xml_params.tag_name.deref();
let pop = if let xmlparser::ElementEnd::Close(prefix, local) = end {
if !xml_params.accepts_children {
return Err(unexpected("children", &local, local));
}
let prefix = prefix.as_str();
let local = local.as_str();
if prefix != "" {
return Err(unexpected("prefix", prefix, span));
}
if local != current_tag {
return Err(unexpected("close tag", local, span));
}
true
} else if let xmlparser::ElementEnd::Empty = end {
true
} else {
false
};
if pop {
if let Some(i) = tree[current].attributes.0.iter().position(|a| a == &AttributeValue::Unset) {
let (attr_name, attr_type, _) = xml_params.attr_set[i];
if required(attr_type) {
return Err(error!("Missing XML attribute: {} (line {})", attr_name, line(span)));
}
}
if let Some(node) = tree.parent(current) {
current = node;
if let Some(index) = tree[current].factory.get() {
xml_params = ordered[usize::from(index)].xml_params.as_ref().unwrap();
}
} else {
return Err(error!("malformed XML: {:?} (line {})", current_tag, line(span)));
}
}
}
else if let Comment { .. } = token {
}
else if let Text { text } = token {
let text_str = text.as_str().trim();
if text_str != "" {
return Err(unexpected("text", text_str, text));
}
}
else {
return Err(error!("Unknown token: {:?}", token));
}
}
let node = match tree.detach_children(current) {
Some(node) => Ok(node),
None => Err(error!("XML file appears to be empty; at least one node is required.")),
}?;
tree.delete(current);
Ok(node)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum AttributeValueType {
SignedPixels = 0,
Pixels,
Ratio,
Other,
OptSignedPixels,
OptPixels,
OptRatio,
OptOther,
}
const fn required(t: AttributeValueType) -> bool {
(t as u8) < 4
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AttributeValue {
OptSignedPixels(Option<SignedPixels>),
SignedPixels(SignedPixels),
OptPixels(Option<Pixels>),
Pixels(Pixels),
OptRatio(Option<Ratio>),
Ratio(Ratio),
OptOther(Option<ArcStr>),
Other(ArcStr),
StateLookup {
namespace: ArcStr,
path: ArcStr,
value_type: AttributeValueType,
},
Unset,
}
impl AttributeValue {
pub fn parse(xml_value: &ArcStr, attr_type: AttributeValueType) -> Result<Self, Error> {
use AttributeValueType::*;
macro_rules! parse_attr {
($xml_value:ident, $variant:ident, $msg:literal, true) => {
match $xml_value.deref().parse() {
Ok(num) => Ok(Self::$variant(num)),
Err(e) => Err(error!("Couldn't parse {} as {}: {}", $xml_value, $msg, e)),
}
};
($xml_value:ident, $variant:ident, $msg:literal, false) => {
match $xml_value.deref().parse() {
Ok(num) => Ok(Self::$variant(Some(num))),
Err(e) => Err(error!("Couldn't parse {} as {}: {}", $xml_value, $msg, e)),
}
};
}
match attr_type {
OptSignedPixels => parse_attr!(xml_value, OptSignedPixels, "a signed number of pixels", false),
SignedPixels => parse_attr!(xml_value, SignedPixels, "a signed number of pixels", true),
OptPixels => parse_attr!(xml_value, OptPixels, "an unsigned number of pixels", false),
Pixels => parse_attr!(xml_value, Pixels, "an unsigned number of pixels", true),
OptRatio => parse_attr!(xml_value, OptRatio, "a ratio", false),
Ratio => parse_attr!(xml_value, Ratio, "a ratio", true),
OptOther => Ok(Self::OptOther(Some(xml_value.clone()))),
Other => Ok(Self::Other(xml_value.clone())),
}
}
}
impl AttributeValueVec {
pub(crate) fn new_import(layout_asset: ArcStr) -> Self {
Self(vec![ AttributeValue::Other(layout_asset) ])
}
pub fn new(params: &XmlTagParameters) -> Self {
let mut vec = Vec::with_capacity(params.attr_set.len());
for (_name, attr_type, default_value) in params.attr_set {
vec.push(match default_value {
Some(xml_value) => AttributeValue::parse(&xml_value, *attr_type).unwrap(),
None => AttributeValue::Unset,
});
}
Self(vec)
}
pub fn get(&self, index: usize) -> &AttributeValue {
self.0.get(index).expect("Invalid Attribute Definition")
}
}
macro_rules! impl_try_from {
($dst_type:ty, $variant:ident) => {
impl TryFrom<AttributeValue> for $dst_type {
type Error = Error;
fn try_from(value: AttributeValue) -> Result<Self, Self::Error> {
match value {
AttributeValue::$variant(inner) => Ok(inner),
_ => panic!("Invalid Attribute Configuration"),
}
}
}
}
}
macro_rules! impl_try_from_opt {
($dst_type:ty, $variant:ident) => {
impl TryFrom<AttributeValue> for $dst_type {
type Error = Error;
fn try_from(value: AttributeValue) -> Result<Self, Self::Error> {
match value {
AttributeValue::$variant(inner) => Ok(inner),
AttributeValue::Unset => Ok(None),
_ => panic!("Invalid Attribute Configuration"),
}
}
}
}
}
impl_try_from_opt!(Option<SignedPixels>, OptSignedPixels);
impl_try_from_opt!(Option<Pixels>, OptPixels);
impl_try_from_opt!(Option<Ratio>, OptRatio);
impl_try_from_opt!(Option<ArcStr>, OptOther);
impl_try_from!(SignedPixels, SignedPixels);
impl_try_from!(Pixels, Pixels);
impl_try_from!(Ratio, Ratio);
impl_try_from!(ArcStr, Other);