use crate::quotes::{DelimitedReferences, QUOTES};
use crate::reference_literal::reference_literal_end;
pub const COMMENT: char = '#';
const SEPARATORS: [u8; 7] = *b" \t\n\r(:)";
const BEFORE_COMMENT: [u8; 4] = *b" \t\n\r";
pub fn strip_comments(document: &str) -> String {
let mut bytes = document.as_bytes().to_vec();
let references = DelimitedReferences::new(document);
let mut position = 0;
let mut token_start = true;
while position < bytes.len() {
let byte = bytes[position];
if byte == COMMENT as u8 && follows(&bytes, position, &BEFORE_COMMENT) {
while position < bytes.len() && bytes[position] != b'\n' && bytes[position] != b'\r' {
bytes[position] = b' ';
position += 1;
}
continue;
}
if SEPARATORS.contains(&byte) {
token_start = true;
position += 1;
continue;
}
if token_start {
if let Some(end) = delimited_token_end(document, position, &references) {
position = end;
continue;
}
}
token_start = false;
position += 1;
}
String::from_utf8(bytes).expect("blanking comment bytes keeps the document valid UTF-8")
}
fn delimited_token_end(
document: &str,
start: usize,
references: &DelimitedReferences,
) -> Option<usize> {
if let Some(end) = reference_literal_end(document, start) {
return Some(end);
}
if QUOTES.contains(&document.as_bytes()[start]) {
return references.end_at(document, start);
}
None
}
fn follows(bytes: &[u8], position: usize, allowed: &[u8]) -> bool {
match position {
0 => true,
_ => allowed.contains(&bytes[position - 1]),
}
}