diff --git a/lib/xml_schema_validator/src/token/cdata.rs b/lib/xml_schema_validator/src/token/cdata.rs index 5e285d4..29b17b5 100644 --- a/lib/xml_schema_validator/src/token/cdata.rs +++ b/lib/xml_schema_validator/src/token/cdata.rs @@ -13,25 +13,23 @@ pub struct CData { /// The different states of CDATA parsing #[derive(Clone, Debug, PartialEq)] enum CDataState { - /// Parsing "<", initial state + /// Buffer contains "<", initial state OpeningBracket1, - /// Parsing "!" after "<" in opening sequence + /// Buffer contains " Vec { + pub fn append(self, c: char) -> Vec { match self.state { CDataState::OpeningBracket1 if c == '!' => vec![Token::CData(Self{state: CDataState::OpeningExclamationPoint, ..self})], CDataState::OpeningExclamationPoint if c == '[' => vec![Token::CData(Self{state: CDataState::OpeningBracket2, ..self})], @@ -59,48 +57,38 @@ impl CData { CDataState::OpeningD if c == 'A' => vec![Token::CData(Self{state: CDataState::OpeningA1, ..self})], CDataState::OpeningA1 if c == 'T' => vec![Token::CData(Self{state: CDataState::OpeningT, ..self})], CDataState::OpeningT if c == 'A' => vec![Token::CData(Self{state: CDataState::OpeningA2, ..self})], - CDataState::OpeningA2 if c == '[' => vec![Token::CData(Self{state: CDataState::OpeningBracket3, ..self})], - CDataState::OpeningBracket3 if c == ']' => vec![Token::CData(Self{state: CDataState::ClosingBracket1, ..self})], - CDataState::OpeningBracket3 if c != ']' => vec![Token::CData(Self{state: CDataState::Content, ..self})], + CDataState::OpeningA2 if c == '[' => vec![Token::CData(Self{state: CDataState::Content, ..self})], + CDataState::Content if c == ']' => vec![Token::CData(Self{state: CDataState::ClosingBracket1, ..self})], + CDataState::Content if c != ']' => vec![Token::CData(Self{state: CDataState::Content, ..self})], CDataState::ClosingBracket1 if c == ']' => vec![Token::CData(Self{state: CDataState::ClosingBracket2, ..self})], + CDataState::ClosingBracket1 if c != ']' => vec![Token::CData(Self{state: CDataState::Content, ..self})], CDataState::ClosingBracket2 if c == '>' => vec![Token::CData(Self{state: CDataState::ClosingBracket3, ..self})], + CDataState::ClosingBracket2 if c != '>' => vec![Token::CData(Self{state: CDataState::Content, ..self})], CDataState::ClosingBracket3 => self.handle_closing_bracket3(c), - CDataState::Content => self.handle_content(c), _ => vec![], } } fn handle_closing_bracket3(self, c: char) -> Vec { + let is_mixed = self + .element + .xs_complex_type + .mixed + .as_ref() + .map(|val| val == "true") + .unwrap_or(false); if '<' == c { - let is_mixed = self - .element - .xs_complex_type - .mixed - .as_ref() - .map(|val| val == "true") - .unwrap_or(false); let mut tokens = vec![Token::ElementCloseStart(ElementCloseStart::new( Arc::clone(&self.element), ))]; - // If mixed content is allowed, also add Comment and CData as possible transitions if is_mixed { tokens.push(Token::Comment(Comment::new(Arc::clone(&self.element)))); tokens.push(Token::CData(CData::new(Arc::clone(&self.element)))); } - tokens } else { - // Check if this element allows mixed content - let is_mixed = self - .element - .xs_complex_type - .mixed - .as_ref() - .map(|val| val == "true") - .unwrap_or(false); - if c.is_whitespace() { vec![Token::CData(self)] } else if is_mixed { @@ -110,11 +98,6 @@ impl CData { } } } - - fn handle_content(self, c: char) -> Vec { - todo!() - } - } #[cfg(test)] @@ -180,11 +163,9 @@ mod tests { #[test] fn test_invalid_cdata_sections() { let values = [ - "", "", - "]]>", // Nesting not allowed + "]]>", "", - " Missing closing bracket", ]; for value in values { diff --git a/lib/xml_schema_validator/src/token/comment.rs b/lib/xml_schema_validator/src/token/comment.rs index 1b436cd..80aab63 100644 --- a/lib/xml_schema_validator/src/token/comment.rs +++ b/lib/xml_schema_validator/src/token/comment.rs @@ -6,10 +6,29 @@ use std::sync::Arc; pub struct Comment { /// The element that contains this comment element: Arc, - /// Buffer containing the comment characters processed so far - buffer: String, - /// Tracks if we're at the end of the comment (checking for -->) - end_dashes: u8, + /// Current parsing state + state: CommentState, +} + +/// The different states of comment parsing +#[derive(Clone, Debug, PartialEq)] +enum CommentState { + /// Buffer contains "<", initial state + OpeningBracket1, + /// Buffer contains "", comment is complete + CommentComplete, } impl Comment { @@ -17,66 +36,58 @@ impl Comment { pub fn new(element: Arc) -> Self { Self { element, - buffer: " Vec { - // Add character to buffer - self.buffer.push(c); - - let buffer_len = self.buffer.len(); - - // Check for comment opening sequence "" - if self.end_dashes == 0 && c == '-' { - self.end_dashes = 1; - return vec![Token::Comment(self)]; - } else if self.end_dashes == 1 { - if c == '-' { - self.end_dashes = 2; - return vec![Token::Comment(self)]; - } else { - // Reset dash count if we didn't see a second dash - self.end_dashes = 0; - return vec![Token::Comment(self)]; + } + + fn handle_comment_complete(self, c: char) -> Vec { + let is_mixed = self + .element + .xs_complex_type + .mixed + .as_ref() + .map(|val| val == "true") + .unwrap_or(false); + if '<' == c { + let mut tokens = vec![Token::ElementCloseStart(ElementCloseStart::new( + Arc::clone(&self.element), + ))]; + + if is_mixed { + tokens.push(Token::Comment(Comment::new(Arc::clone(&self.element)))); + tokens.push(Token::CData(CData::new(Arc::clone(&self.element)))); } - } else if self.end_dashes == 2 { - if c == '>' { - // Comment is complete, transition back to text content - return vec![Token::TextContent(TextContent::new(self.element))]; - } else if c == '-' { - // -- followed by another - is not allowed in XML comments - // but we'll be lenient and just keep the dash count at 2 - return vec![Token::Comment(self)]; + tokens + } else { + if c.is_whitespace() { + vec![Token::Comment(self)] + } else if is_mixed { + vec![Token::TextContent(TextContent::new(self.element))] } else { - // Reset dash count if we didn't see a closing bracket - self.end_dashes = 0; - return vec![Token::Comment(self)]; + vec![] } } - - // Regular comment content - vec![Token::Comment(self)] } } + #[cfg(test)] mod tests { use super::*; @@ -90,7 +101,6 @@ mod tests { "", "", "", - "", // Empty comment ]; for value in values { @@ -136,11 +146,8 @@ mod tests { #[test] fn test_invalid_comments() { let values = [ - " -->", // Nesting not allowed + " -->", "", - " Malformed comment", - " Suspicious triple dash", ]; for value in values { diff --git a/lib/xml_schema_validator/src/token/text_content.rs b/lib/xml_schema_validator/src/token/text_content.rs index ff34e97..344c0bb 100644 --- a/lib/xml_schema_validator/src/token/text_content.rs +++ b/lib/xml_schema_validator/src/token/text_content.rs @@ -33,6 +33,8 @@ impl TextContent { } tokens + } else if c == '>' { + vec![] } else { vec![Token::TextContent(TextContent::new(Arc::clone( &self.element,