use super::{ ast::DiscordComponent, parse_code::*, parse_inline_style::*, parse_link::*, parse_quotes::*, }; pub fn parse(text: &'_ str) -> Vec> { let mut tokens = Vec::new(); let mut working_plain_start: isize = -1; let mut i = 0; while i < text.len() { let is_line_start = matches!(tokens.last(), None | Some(DiscordComponent::LineBreak)); match parse_token(&text[i..], is_line_start) { Some((token, consumed)) => { if working_plain_start >= 0 { let plain_start = working_plain_start as usize; tokens.push(DiscordComponent::Plain(&text[plain_start..i])); working_plain_start = -1; } tokens.push(token); i += consumed; } None => { if working_plain_start < 0 { working_plain_start = i as isize; } let mut next_char = i + 1; while !text.is_char_boundary(next_char) { next_char += 1; } i = next_char; } } } if working_plain_start >= 0 { let plain_start = working_plain_start as usize; tokens.push(DiscordComponent::Plain(&text[plain_start..])); } tokens } pub fn parse_token(text: &'_ str, is_line_start: bool) -> Option<(DiscordComponent<'_>, usize)> { parse_escaped_literal(text) .or_else(|| is_line_start.then(|| parse_quotes(text)).flatten()) .or_else(|| parse_code_block(text)) .or_else(|| parse_code(text)) .or_else(|| parse_link(text)) .or_else(|| parse_bold(text)) .or_else(|| parse_italic(text)) .or_else(|| parse_strikethrough(text)) .or_else(|| parse_underline(text)) .or_else(|| parse_spoiler(text)) .or_else(|| parse_line_break(text)) } pub fn parse_escaped_literal(text: &'_ str) -> Option<(DiscordComponent<'_>, usize)> { let mut chars = text.chars(); if let Some('\\') = chars.next() { return match chars.next() { Some(x @ '\\') | Some(x @ '`') | Some(x @ '*') | Some(x @ '_') | Some(x @ '{') | Some(x @ '}') | Some(x @ '[') | Some(x @ ']') | Some(x @ '(') | Some(x @ ')') | Some(x @ '#') | Some(x @ '+') | Some(x @ '-') | Some(x @ '.') | Some(x @ '!') => { Some((DiscordComponent::Literal(x), 2)) } _ => None, }; } None } pub fn parse_line_break(text: &'_ str) -> Option<(DiscordComponent<'_>, usize)> { text.starts_with('\n') .then(|| (DiscordComponent::LineBreak, 1)) }