use crate::attrmap2::AttrMap2;
use crate::text::TextP;
use crate::OdsError;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, Default)]
pub struct XmlTag {
name: String,
attr: AttrMap2,
content: Vec<XmlContent>,
}
impl From<&str> for XmlTag {
fn from(name: &str) -> Self {
XmlTag::new(name)
}
}
impl From<String> for XmlTag {
fn from(name: String) -> Self {
XmlTag::new(name)
}
}
pub trait XmlVec {
fn add_text<S: Into<String>>(&mut self, txt: S);
fn add_tag<T: Into<XmlTag>>(&mut self, tag: T);
}
impl XmlVec for &mut Vec<XmlTag> {
fn add_text<S: Into<String>>(&mut self, txt: S) {
self.push(TextP::new().text(txt).into());
}
fn add_tag<T: Into<XmlTag>>(&mut self, tag: T) {
self.push(tag.into());
}
}
impl XmlTag {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
attr: Default::default(),
content: vec![],
}
}
pub fn set_name<S: Into<String>>(&mut self, name: S) {
self.name = name.into();
}
pub fn name(&self) -> &str {
&self.name
}
pub fn is_empty(&self) -> bool {
self.content.is_empty()
}
pub(crate) fn attrmap(&self) -> &AttrMap2 {
&self.attr
}
pub(crate) fn attrmap_mut(&mut self) -> &mut AttrMap2 {
&mut self.attr
}
pub fn set_attr<'a, S: Into<&'a str>, T: Into<String>>(&mut self, name: S, value: T) {
self.attr.set_attr(name.into(), value.into());
}
pub fn add_attr_slice(&mut self, attr: &[(&str, String)]) {
self.attr.add_all(attr);
}
pub fn add_tag<T: Into<XmlTag>>(&mut self, tag: T) {
self.content.push(XmlContent::Tag(tag.into()));
}
pub fn pop_tag(&mut self) -> Option<XmlTag> {
match self.content.get(0) {
None => None,
Some(XmlContent::Tag(_)) => {
if let XmlContent::Tag(tag) = self.content.pop().unwrap() {
Some(tag)
} else {
unreachable!()
}
}
Some(XmlContent::Text(_)) => None,
}
}
pub fn add_text<S: Into<String>>(&mut self, text: S) {
self.content.push(XmlContent::Text(text.into()));
}
pub fn attr<'a, S: Into<&'a str>, T: Into<String>>(mut self, name: S, value: T) -> Self {
self.set_attr(name, value);
self
}
pub fn attr_slice(mut self, attr: &[(&str, String)]) -> Self {
self.add_attr_slice(attr);
self
}
pub fn tag<T: Into<XmlTag>>(mut self, tag: T) -> Self {
self.add_tag(tag);
self
}
pub fn text<S: Into<String>>(mut self, text: S) -> Self {
self.add_text(text);
self
}
pub fn content(&self) -> &Vec<XmlContent> {
&self.content
}
pub fn content_mut(&mut self) -> &mut Vec<XmlContent> {
&mut self.content
}
pub fn extract_text(&self, buf: &mut String) {
for c in &self.content {
match c {
XmlContent::Text(t) => {
buf.push_str(t.as_str());
}
XmlContent::Tag(t) => {
t.extract_text(buf);
}
}
}
}
pub fn into_vec(self) -> Result<Vec<XmlTag>, OdsError> {
let mut content = Vec::new();
for c in self.content {
match c {
XmlContent::Text(v) => {
return Err(OdsError::Parse(format!("Unexpected literal text '{}'", v)))
}
XmlContent::Tag(v) => content.push(v),
}
}
Ok(content)
}
}
impl Display for XmlTag {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "<{}", self.name)?;
for (n, v) in self.attr.iter() {
write!(f, " {}=\"{}\"", n, v)?;
}
if self.content.is_empty() {
writeln!(f, "/>")?;
} else {
writeln!(f, ">")?;
for c in &self.content {
match c {
XmlContent::Text(t) => {
writeln!(f, "{}", t)?;
}
XmlContent::Tag(t) => {
t.fmt(f)?;
}
}
}
writeln!(f, "</{}>", self.name)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
#[allow(variant_size_differences)]
pub enum XmlContent {
Text(String),
Tag(XmlTag),
}