#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum LineType {
Header,
Paragraph,
HorizontalRule,
BlockQuote,
Html(HtmlType),
Preformatted,
Table,
UL(usize, ULType),
OL(usize, i32),
FrontMatter,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HtmlType {
Normal,
Comment,
CData,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ULType {
Star,
Minus,
Plus,
}
pub fn block_line_type(line: &str, excluded: &[String]) -> LineType {
let trimmed = line.trim();
if !excluded.contains(&"h".to_string()) && trimmed.starts_with('#') {
LineType::Header
}
else if !excluded.contains(&"hr".to_string()) && trimmed.starts_with("----") && line.replace(['-', ' ', '\t'], "") == "" {
LineType::HorizontalRule
} else if line.starts_with('>') {
if line.starts_with(">!") {
return LineType::Paragraph;
}
if !excluded.contains(&"blockquote".to_string()) {
LineType::BlockQuote
} else {
LineType::Paragraph
}
} else if !excluded.contains(&"pre".to_string()) && trimmed.starts_with("```") {
LineType::Preformatted
} else if trimmed.starts_with('<') {
if trimmed.starts_with("<!--") && !excluded.contains(&"html_comment".to_string()) {
return LineType::Html(HtmlType::Comment);
}
if trimmed.starts_with("<![CDATA[") && !excluded.contains(&"html_cdata".to_string()) {
return LineType::Html(HtmlType::CData);
}
let mut char_array = trimmed.chars();
char_array.next();
if let Some(ch) = char_array.next() {
match ch {
'<' => {
return LineType::Paragraph;
}
'a'..='z' | 'A'..='Z' | '0'..='9' => {
}
_ => {
return LineType::Paragraph;
}
}
} else {
return LineType::Paragraph;
}
if !excluded.contains(&"html".to_string()) {
LineType::Html(HtmlType::Normal)
} else {
LineType::Paragraph
}
} else if !excluded.contains(&"table".to_string()) && trimmed.starts_with('|') {
LineType::Table
}
else if !excluded.contains(&"ul".to_string()) && (trimmed.starts_with("- ") || trimmed.starts_with("+ ") || trimmed.starts_with("* ")) {
let mut indent = 2;
for character in line.chars() {
match character {
' ' => {
indent += 1;
}
_ => break,
}
}
let list_type = if trimmed.starts_with("* ") {
ULType::Star
} else if trimmed.starts_with("+ ") {
ULType::Plus
} else if trimmed.starts_with("- ") {
ULType::Minus
} else {
return LineType::Paragraph;
};
LineType::UL(indent, list_type)
}
else if !excluded.contains(&"fm".to_string()) && trimmed.starts_with("+++") {
LineType::FrontMatter
}
else {
let characters: Vec<char> = line.chars().collect();
let mut index = 0;
loop {
if index >= characters.len() {
break;
}
let character = characters[index];
match character {
' ' => {
}
_ => break,
}
index += 1;
}
let mut numbers: Vec<char> = vec![];
loop {
if index >= characters.len() {
break;
}
let character = characters[index];
match character {
'0'..='9' => {
numbers.push(character);
}
_ => break,
}
index += 1;
}
if numbers.is_empty() {
return LineType::Paragraph;
}
if numbers.len() > 9 {
return LineType::Paragraph;
}
let mut dot = false;
if index < characters.len() {
let character = characters[index];
if character == '.' {
dot = true;
}
if character == ')' {
dot = true;
}
index += 1;
}
if !dot {
return LineType::Paragraph;
}
let mut space = false;
if index < characters.len() {
let character = characters[index];
if character == ' ' {
space = true;
}
index += 1;
}
if !space {
return LineType::Paragraph;
}
let number_str: String = numbers.into_iter().collect();
let number: i32 = number_str.parse().unwrap_or(0);
LineType::OL(index, number)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_batch(inputs: Vec<(&str, bool)>, test_type: LineType) {
for (input, result) in inputs {
if result {
assert_eq!(block_line_type(input, &[]), test_type);
} else {
assert_ne!(block_line_type(input, &[]), test_type);
}
}
}
#[test]
fn header() {
let inputs = vec![("#", true), ("##", true), ("###", true), ("####", true), ("#####", true), ("######", true), ("#######", true), (" #", true), (" ##", true), (" ###", true), (" ####", true), (" #####", true), ("######", true)];
test_batch(inputs, LineType::Header);
}
#[test]
fn hr() {
let inputs = vec![("-", false), ("--", false), ("---", false), ("----", true), ("---- -", true), ("- ---- -", false), (" ---- -", true)];
test_batch(inputs, LineType::HorizontalRule);
}
#[test]
fn blockquote() {
let inputs = vec![(">", true), (">>", true), (">>>", true), (">!", false), (" >", false)];
test_batch(inputs, LineType::BlockQuote);
}
}