use crate::err::Error;
use crate::{Node, Serde};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Extra<'e> {
pub end: bool,
pub pos: usize,
pub tag: &'e str,
}
#[derive(Eq, PartialEq)]
enum ChildrenProcess {
BeginTag,
CloseTag,
None,
Plain,
}
enum TagProcess {
Attrs,
Quote,
None,
Tag,
}
fn rde<'t>(
h: &'t str,
pre: Option<Weak<RefCell<Node>>>,
) -> Result<(Rc<RefCell<Node>>, Option<Extra<'t>>), Error> {
let mut pos = 0_usize;
if h.is_empty() {
return Ok((Rc::new(RefCell::new(Node::default())), None));
} else if h.find("</").is_none() {
return Ok((Rc::new(RefCell::new(self::plain(h, pre))), None));
}
let tree = Rc::new(RefCell::new(Node::default()));
let tw = Rc::downgrade(&tree);
let (tag, attrs) = self::tag(&h[pos..], &mut pos)?;
let mut children: Vec<Rc<RefCell<Node>>> = vec![];
let mut cext = self::ch(&h[pos..], Some(tw.clone()), tag, &mut children)?;
pos += cext.pos;
while !cext.end {
cext = self::ch(&h[pos..], Some(tw.clone()), tag, &mut children)?;
pos += cext.pos;
}
let ext = if (pos + 1) != h.len() {
Some(Extra {
end: false,
pos,
tag: cext.tag,
})
} else {
None
};
let mut bt = tree.borrow_mut();
bt.pre = pre;
bt.tag = tag.to_string();
bt.attrs = attrs;
bt.children = children;
drop(bt);
Ok((tree, ext))
}
fn ch<'t>(
cht: &'t str,
pre: Option<Weak<RefCell<Node>>>,
tag: &'t str,
children: &mut Vec<Rc<RefCell<Node>>>,
) -> Result<Extra<'t>, Error> {
let mut itag = tag;
let mut process = ChildrenProcess::None;
let (mut t, mut c) = ((0, 0), (0, 0));
for (p, q) in cht.chars().enumerate() {
match q {
'<' => {
if process == ChildrenProcess::Plain {
c.1 = p;
}
process = ChildrenProcess::BeginTag;
t.0 = p;
t.1 = p;
}
'/' => {
if &cht[t.0..(t.0 + 1)] == "<" {
process = ChildrenProcess::CloseTag;
} else if process != ChildrenProcess::Plain {
return Err(Error::DeserializeHtmlError(format!(
"children parse failed {}, cht: {}, process: {}",
&tag,
&cht,
&cht[t.0..(t.0 + 1)],
)));
}
}
'>' => {
t.1 = p;
match process {
ChildrenProcess::BeginTag => {
let (tree, ext) = self::rde(&cht[t.0..], pre.clone())?;
children.push(tree);
if let Some(cext) = ext {
return Ok(Extra {
end: false,
tag: cext.tag,
pos: cext.pos + t.0,
});
}
}
ChildrenProcess::CloseTag => {
itag = &cht[(t.0 + 1)..t.1].trim()[1..].trim()[..];
if itag != tag {
return Err(Error::DeserializeHtmlError(format!(
"children parse failed {}, cht: {}, close_tag: {}",
&tag, &cht, &itag
)));
} else if !cht[c.0..c.1].is_empty() {
children.push(Rc::new(RefCell::new(self::plain(&cht[c.0..c.1], pre))));
}
return Ok(Extra {
end: true,
pos: p,
tag: itag,
});
}
_ => {
}
}
}
x if !x.is_whitespace() => {
match process {
ChildrenProcess::None => {
process = ChildrenProcess::Plain;
c.0 = p;
c.1 = p;
}
ChildrenProcess::Plain => {
c.1 = p;
}
_ => {
}
}
}
_ => {
}
}
}
Ok(Extra {
end: true,
pos: cht.len(),
tag: itag,
})
}
fn plain<'t>(h: &'t str, pre: Option<Weak<RefCell<Node>>>) -> Node {
let mut attrs = HashMap::<String, String>::new();
attrs.insert("text".into(), h.into());
Node {
pre,
tag: "plain".into(),
attrs,
children: vec![],
}
}
fn tag<'t>(h: &'t str, pos: &mut usize) -> Result<(&'t str, HashMap<String, String>), Error> {
let (mut tag, mut key, mut value) = ((0, 0), (0, 0), (0, 0));
let mut attrs = HashMap::<String, String>::new();
let mut process = TagProcess::None;
for (p, q) in h.chars().enumerate() {
match q {
'<' => {
process = TagProcess::Tag;
tag.0 = p + 1;
tag.1 = p + 1;
}
'>' => {
match process {
TagProcess::Tag => tag.1 = p,
TagProcess::Attrs => {
if !&h[key.0..key.1].trim().is_empty() {
attrs.insert(
h[key.0..key.1].trim().to_string(),
h[value.0..value.1].trim().into(),
);
}
}
_ => {}
}
*pos = *pos + p + 1;
return Ok((&h[tag.0..tag.1].trim(), attrs));
}
'"' => match process {
TagProcess::Quote => {
process = TagProcess::Attrs;
value.1 = p;
}
_ => {
value.0 = p + 1;
value.1 = p + 1;
process = TagProcess::Quote;
}
},
'=' => match process {
TagProcess::Attrs => key.1 = p,
_ => {
return Err(Error::DeserializeHtmlError(format!(
"html tag parse failed: {}, html: {}",
&h[tag.0..tag.1],
&h
)))
}
},
x if x.is_whitespace() => match process {
TagProcess::Tag => {
if h[tag.0..tag.1].trim().is_empty() {
tag.1 = p;
} else {
process = TagProcess::Attrs;
key.0 = p + 1;
key.1 = p + 1;
}
}
TagProcess::Quote => {
value.1 = p;
}
TagProcess::Attrs => {
if (key.1 - key.0 != 0) && (value.1 - value.0 != 0) {
attrs.insert(
h[key.0..key.1].trim().to_string(),
h[value.0..value.1].trim().into(),
);
key.0 = p;
key.1 = p;
}
}
_ => {}
},
x if !x.is_whitespace() => match process {
TagProcess::Tag => {
tag.1 = p + 1;
}
TagProcess::Quote => {
value.1 = p;
}
TagProcess::Attrs => {
if value.0 == 0 {
key.1 = p;
} else {
value.1 = p;
}
}
_ => {}
},
_ => {
return Err(Error::DeserializeHtmlError(format!(
"html tag parse failed: {}, html: {}, char: {}",
&h[tag.0..tag.1],
&h,
&q
)))
}
}
}
Err(Error::DeserializeHtmlError(format!(
"html tag parse failed: {}, html: {}",
&h[tag.0..tag.1],
&h
)))
}
impl<'t> Serde<Node, String, Error> for Node {
fn de(h: String) -> Result<Node, Error> {
Ok(self::rde(Box::leak(Box::new(h)), None)?
.0
.borrow()
.to_owned())
}
fn ser(&self) -> String {
let mut html = "".to_string();
let mut attrs = " ".to_string();
let mut children = "".to_string();
if self.tag == "plain" {
html.push_str(&self.attrs.get("text").unwrap_or(&"".into()));
} else {
for (k, v) in self.attrs.iter() {
attrs.push_str(&format!("{}=\"{}\" ", k, v));
}
for i in &self.children {
children.push_str(&i.borrow().to_owned().ser());
}
if attrs.trim().is_empty() {
attrs.drain(..);
}
html.push_str(&format!(
"<{}{}>{}</{}>",
&self.tag,
attrs.trim_end(),
children,
&self.tag,
));
}
html
}
}