Source code
Revision control
Copy as Markdown
Other Tools
//! Contains a parser for an XML comment.↩
↩
use crate::errors::SyntaxError;↩
use crate::parser::Parser;↩
↩
/// A parser that search a `-->` sequence in the slice.↩
///↩
/// To use a parser create an instance of parser and [`feed`] data into it.↩
/// After successful search the parser will return [`Some`] with position where↩
/// comment is ended (the position after `-->`). If search was unsuccessful,↩
/// a [`None`] will be returned. You typically would expect positive result of↩
/// search, so that you should feed new data until yo'll get it.↩
///↩
/// NOTE: after successful match the parser does not returned to the initial↩
/// state and should not be used anymore. Create a new parser if you want to perform↩
/// new search.↩
///↩
/// # Example↩
///↩
/// ```↩
/// # use pretty_assertions::assert_eq;↩
/// use quick_xml::parser::{CommentParser, Parser};↩
///↩
/// let mut parser = CommentParser::default();↩
///↩
/// // Parse `<!-- comment with some -> and --- inside-->and the text follow...`↩
/// // splitted into three chunks↩
/// assert_eq!(parser.feed(b"<!-- comment"), None);↩
/// // ...get new chunk of data↩
/// assert_eq!(parser.feed(b" with some -> and -"), None);↩
/// // ...get another chunk of data↩
/// assert_eq!(parser.feed(b"-- inside-->and the text follow..."), Some(12));↩
/// // ^ ^↩
/// // 0 11↩
/// ```↩
///↩
/// [`feed`]: Self::feed()↩
#[derive(Clone, Copy, Debug, Eq, PartialEq)]↩
pub enum CommentParser {↩
/// The parser does not yet seen any dashes at the end of previous slice.↩
Seen0,↩
/// The parser already seen one dash on the end of previous slice.↩
Seen1,↩
/// The parser already seen two dashes on the end of previous slice.↩
Seen2,↩
}↩
↩
impl Default for CommentParser {↩
#[inline]↩
fn default() -> Self {↩
Self::Seen0↩
}↩
}↩
↩
impl Parser for CommentParser {↩
/// Determines the end position of an XML comment in the provided slice.↩
/// Comments is a pieces of text enclosed in `<!--` and `-->` braces.↩
/// Comment ends on the first occurrence of `-->` which cannot be escaped.↩
///↩
/// Returns position after the `-->` or `None` if such sequence was not found.↩
///↩
/// # Parameters↩
/// - `bytes`: a slice to find the end of a comment.↩
/// Should contain text in ASCII-compatible encoding↩
#[inline]↩
fn feed(&mut self, bytes: &[u8]) -> Option<usize> {↩
let result = match self {↩
Self::Seen0 => seen0(bytes),↩
Self::Seen1 => seen1(bytes),↩
Self::Seen2 => seen2(bytes),↩
};↩
if let Some(r) = result {↩
return Some(r);↩
}↩
if bytes.ends_with(b"--") {↩
*self = Self::Seen2;↩
} else {↩
self.next_state(bytes.last().copied());↩
}↩
None↩
}↩
↩
#[inline]↩
fn eof_error(self, _content: &[u8]) -> SyntaxError {↩
SyntaxError::UnclosedComment↩
}↩
}↩
↩
impl CommentParser {↩
#[inline]↩
fn next_state(&mut self, last: Option<u8>) {↩
match (*self, last) {↩
(Self::Seen0, Some(b'-')) => *self = Self::Seen1,↩
↩
(Self::Seen1, Some(b'-')) => *self = Self::Seen2,↩
(Self::Seen1, Some(_)) => *self = Self::Seen0,↩
↩
(Self::Seen2, Some(b'-')) => {}↩
(Self::Seen2, Some(_)) => *self = Self::Seen0,↩
↩
_ => {}↩
}↩
}↩
}↩
↩
#[inline]↩
fn seen0(bytes: &[u8]) -> Option<usize> {↩
for i in memchr::memchr_iter(b'>', bytes) {↩
if bytes[..i].ends_with(b"--") {↩
// +1 for `>` which should be included in event↩
return Some(i + 1);↩
}↩
}↩
None↩
}↩
↩
#[inline]↩
fn seen1(bytes: &[u8]) -> Option<usize> {↩
// -|->↩
if bytes.starts_with(b"->") {↩
return Some(2);↩
}↩
// Even if the first character is `-` it cannot be part of close sequence,↩
// because we checked that condition above. That means that we can forgot that↩
// we seen one `-` at the end of the previous chunk.↩
// -|x...↩
seen0(bytes)↩
}↩
↩
#[inline]↩
fn seen2(bytes: &[u8]) -> Option<usize> {↩
match bytes.first() {↩
// --|↩
None => None,↩
// --|>↩
Some(b'>') => Some(1),↩
// The end sequence here can be matched only if bytes starts with `->`↩
// which is handled in seen1().↩
// --|x...↩
Some(_) => seen1(bytes),↩
}↩
}↩
↩
#[test]↩
fn parse() {↩
use pretty_assertions::assert_eq;↩
use CommentParser::*;↩
↩
/// Returns `Ok(pos)` with the position in the buffer where element is ended.↩
///↩
/// Returns `Err(internal_state)` if parsing was not done yet.↩
fn parse_comment(bytes: &[u8], mut parser: CommentParser) -> Result<usize, CommentParser> {↩
match parser.feed(bytes) {↩
Some(i) => Ok(i),↩
None => Err(parser),↩
}↩
}↩
↩
assert_eq!(parse_comment(b"", Seen0), Err(Seen0)); // xx|↩
assert_eq!(parse_comment(b"", Seen1), Err(Seen1)); // x-|↩
assert_eq!(parse_comment(b"", Seen2), Err(Seen2)); // --|↩
↩
assert_eq!(parse_comment(b"-", Seen0), Err(Seen1)); // xx|-↩
assert_eq!(parse_comment(b"-", Seen1), Err(Seen2)); // x-|-↩
assert_eq!(parse_comment(b"-", Seen2), Err(Seen2)); // --|-↩
↩
assert_eq!(parse_comment(b">", Seen0), Err(Seen0)); // xx|>↩
assert_eq!(parse_comment(b">", Seen1), Err(Seen0)); // x-|>↩
assert_eq!(parse_comment(b">", Seen2), Ok(1)); // --|>↩
↩
assert_eq!(parse_comment(b"--", Seen0), Err(Seen2)); // xx|--↩
assert_eq!(parse_comment(b"--", Seen1), Err(Seen2)); // x-|--↩
assert_eq!(parse_comment(b"--", Seen2), Err(Seen2)); // --|--↩
↩
assert_eq!(parse_comment(b"->", Seen0), Err(Seen0)); // xx|->↩
assert_eq!(parse_comment(b"->", Seen1), Ok(2)); // x-|->↩
assert_eq!(parse_comment(b"->", Seen2), Ok(2)); // --|->↩
↩
assert_eq!(parse_comment(b"-->", Seen0), Ok(3)); // xx|-->↩
assert_eq!(parse_comment(b"-->", Seen1), Ok(3)); // x-|-->↩
assert_eq!(parse_comment(b"-->", Seen2), Ok(3)); // --|-->↩
↩
assert_eq!(parse_comment(b">-->", Seen0), Ok(4)); // xx|>-->↩
assert_eq!(parse_comment(b">-->", Seen1), Ok(4)); // x-|>-->↩
assert_eq!(parse_comment(b">-->", Seen2), Ok(1)); // --|>-->↩
↩
assert_eq!(parse_comment(b"->-->", Seen0), Ok(5)); // xx|->-->↩
assert_eq!(parse_comment(b"->-->", Seen1), Ok(2)); // x-|->-->↩
assert_eq!(parse_comment(b"->-->", Seen2), Ok(2)); // --|->-->↩
}↩