88 lines
2.7 KiB
Rust
88 lines
2.7 KiB
Rust
use super::{
|
|
ast::DiscordComponent, parse_code::*, parse_inline_style::*, parse_link::*, parse_quotes::*,
|
|
};
|
|
|
|
pub fn parse(text: &'_ str) -> Vec<DiscordComponent<'_>> {
|
|
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(|| {
|
|
if is_line_start {
|
|
parse_quotes(text)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.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)> {
|
|
if text.starts_with('\n') {
|
|
return Some((DiscordComponent::LineBreak, 1));
|
|
}
|
|
|
|
None
|
|
}
|