use crate::mdx_parser::MdxSignal;
use crate::mdx_parser::event::Name;
use crate::mdx_parser::message;
use crate::mdx_parser::state::{Name as StateName, State};
use crate::mdx_parser::tokenizer::Tokenizer;
use crate::mdx_parser::util::location::Location;
use crate::mdx_parser::util::{
mdx_collect::{charge_parse_budget, collect_new, collected, reset_collect},
slice::Slice,
};
use alloc::boxed::Box;
pub fn start(tokenizer: &mut Tokenizer) -> State {
if tokenizer.parse_state.options.constructs.mdx_esm
&& tokenizer.parse_state.options.mdx_esm_parse.is_some()
&& !tokenizer.interrupt
&& tokenizer.point.column == 1
&& matches!(tokenizer.current, Some(b'e' | b'i'))
{
tokenizer.tokenize_state.start = tokenizer.point.index;
tokenizer.enter(Name::MdxEsm);
tokenizer.enter(Name::MdxEsmData);
tokenizer.consume();
State::Next(StateName::MdxEsmWord)
} else {
State::Nok
}
}
pub fn word(tokenizer: &mut Tokenizer) -> State {
if matches!(tokenizer.current, Some(b'a'..=b'z')) {
tokenizer.consume();
State::Next(StateName::MdxEsmWord)
} else {
let slice = Slice::from_indices(
tokenizer.parse_state.bytes,
tokenizer.tokenize_state.start,
tokenizer.point.index,
);
if matches!(slice.as_str(), "export" | "import") && tokenizer.current == Some(b' ') {
tokenizer.concrete = true;
tokenizer.tokenize_state.start = tokenizer.events.len() - 1;
reset_collect(tokenizer);
tokenizer.consume();
State::Next(StateName::MdxEsmInside)
} else {
tokenizer.tokenize_state.start = 0;
State::Nok
}
}
}
pub fn inside(tokenizer: &mut Tokenizer) -> State {
match tokenizer.current {
None | Some(b'\n') => {
tokenizer.exit(Name::MdxEsmData);
State::Retry(StateName::MdxEsmLineStart)
}
_ => {
tokenizer.consume();
State::Next(StateName::MdxEsmInside)
}
}
}
pub fn line_start(tokenizer: &mut Tokenizer) -> State {
match tokenizer.current {
None => State::Retry(StateName::MdxEsmAtEnd),
Some(b'\n') => {
tokenizer.check(
State::Next(StateName::MdxEsmAtEnd),
State::Next(StateName::MdxEsmContinuationStart),
);
State::Retry(StateName::MdxEsmBlankLineBefore)
}
_ => {
tokenizer.enter(Name::MdxEsmData);
tokenizer.consume();
State::Next(StateName::MdxEsmInside)
}
}
}
pub fn continuation_start(tokenizer: &mut Tokenizer) -> State {
tokenizer.enter(Name::LineEnding);
tokenizer.consume();
tokenizer.exit(Name::LineEnding);
State::Next(StateName::MdxEsmLineStart)
}
pub fn blank_line_before(tokenizer: &mut Tokenizer) -> State {
tokenizer.enter(Name::LineEnding);
tokenizer.consume();
tokenizer.exit(Name::LineEnding);
State::Next(StateName::BlankLineStart)
}
pub fn at_end(tokenizer: &mut Tokenizer) -> State {
let result = parse_esm(tokenizer);
if matches!(result, State::Ok) {
tokenizer.concrete = false;
tokenizer.exit(Name::MdxEsm);
}
result
}
fn parse_esm(tokenizer: &mut Tokenizer) -> State {
let parse = tokenizer.parse_state.options.mdx_esm_parse.as_ref().unwrap();
collect_new(tokenizer, &[Name::MdxEsmData, Name::LineEnding]);
let place = &tokenizer.events[tokenizer.tokenize_state.start].point;
let result = collected(tokenizer);
if let Some(error) = charge_parse_budget(tokenizer, result.value.len(), place) {
return error;
}
match parse(&result.value) {
MdxSignal::Ok => State::Ok,
MdxSignal::Error(message, relative, source, rule_id) => {
let point = Location::new(tokenizer.parse_state.bytes)
.relative_to_point(&result.stops, relative)
.expect("expected non-empty string");
State::error(message::Message {
place: Some(Box::new(message::Place::Point(point))),
reason: message,
source,
rule_id,
})
}
MdxSignal::Eof(message, source, rule_id) => {
if tokenizer.current.is_none() {
State::error(message::Message {
place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))),
reason: message,
source,
rule_id,
})
} else {
tokenizer.tokenize_state.mdx_last_parse_error = Some((message, *source, *rule_id));
State::Retry(StateName::MdxEsmContinuationStart)
}
}
}
}