use core::fmt; pub struct Html { pub nodes: Vec, } impl Html { pub fn parse>(raw: S) -> Self { let mut raw = raw.as_ref(); let mut nodes = vec![]; loop { let Consumed { node, remaining } = Self::parse_node(raw); nodes.push(node); match remaining { None => break Self { nodes }, Some(rem) => raw = rem, } } } fn parse_node(raw: &str) -> Consumed { match Self::is_tag(raw) { Some(_) => { if let Some(cmt) = Self::parse_comment(raw) { cmt } else { Self::parse_tag(raw) } } None => { let cons = Self::parse_text(raw); cons } } } fn parse_tag(raw: &str) -> Consumed { let (root_tag, mut rest) = Self::is_tag(raw).unwrap(); //println!("- {raw}"); if root_tag.closing { panic!( "found closing tag when not expected! {:?}\n{raw}", root_tag.name ) } else if root_tag.self_closing { return Consumed { node: Node::Tag { self_closing: true, name: root_tag.name.into(), children: vec![], }, remaining: rest, }; } let mut children = vec![]; loop { // Special case "; let special = Html::special_parse(basic, "script"); assert_eq!(special.unwrap().0, "words words\n"); assert!(special.unwrap().1.is_empty()); } #[test] fn special_parse_correctly_ignore_non_start() { let nonstart = "first_line\nlet end = '';\n"; let special = Html::special_parse(nonstart, "script"); assert!(special.is_none()); } #[test] fn special_parse_correctly_handles_leading_whitespace() { let white = "words words\n \t\t"; let special = Html::special_parse(white, "script"); assert_eq!(special.unwrap().0, "words words\n \t\t"); } #[test] fn parse_node_parses_comment() { let cmt = ""; let node = Html::parse_node(cmt); assert_eq!(node.node, comment!(" Comment! ")); } #[test] fn parse_node_parses_tag() { let basic = "

Hello!

"; let hh = Html::parse_node(basic); assert_eq!( hh.node, Node::Tag { self_closing: false, name: "p".into(), children: vec![text!("Hello!")] } ) } #[test] fn parse_node_parses_nested_tags() { let nested = "

Hello!

"; let hh = Html::parse_node(nested); assert_eq!( hh.node, Node::Tag { self_closing: false, name: "p".into(), children: vec![Node::Tag { self_closing: false, name: "p".into(), children: vec![text!("Hello!")] }] } ) } #[test] fn parse_multiple_toplevel() { let nested = "

Hello

World!

"; let hh = Html::parse(nested); assert_eq!( hh.nodes, vec![ Node::Tag { self_closing: false, name: "p".into(), children: vec![text!("Hello ")] }, Node::Tag { self_closing: false, name: "p".into(), children: vec![text!("World!")] } ] ) } }