use super::EventsParser;
use crate::parser::{
ast::Section, errors::ParseIssue, position_tracker::PositionTracker,
sections::SectionParseResult, ParseResult,
};
use alloc::vec::Vec;
impl<'a> EventsParser<'a> {
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn new(source: &'a str, start_position: usize, start_line: usize) -> Self {
Self {
tracker: PositionTracker::new_at(
source,
start_position,
u32::try_from(start_line).unwrap_or(u32::MAX),
1,
),
issues: Vec::new(),
format: None,
}
}
#[must_use]
pub fn with_format(
source: &'a str,
format: &[&'a str],
start_position: usize,
start_line: u32,
) -> Self {
Self {
tracker: PositionTracker::new_at(source, start_position, start_line, 1),
issues: Vec::new(),
format: Some(format.to_vec()),
}
}
pub fn parse(mut self) -> ParseResult<SectionParseResult<'a>> {
let mut events = Vec::new();
while !self.tracker.is_at_end() && !self.at_next_section() {
self.skip_whitespace_and_comments();
if self.tracker.is_at_end() || self.at_next_section() {
break;
}
let line_start = self.tracker.checkpoint();
let line = self.current_line().trim();
if line.is_empty() {
self.tracker.skip_line();
continue;
}
if line.starts_with("Format:") {
self.parse_format_line(line);
} else if let Some(event) = self.parse_event_line_internal(line, &line_start) {
events.push(event);
}
self.tracker.skip_line();
}
Ok((
Section::Events(events),
self.format,
self.issues,
self.tracker.offset(),
self.tracker.line() as usize,
))
}
fn parse_format_line(&mut self, line: &'a str) {
if let Some(format_data) = line.strip_prefix("Format:") {
let fields: Vec<&'a str> = format_data.split(',').map(str::trim).collect();
self.format = Some(fields);
}
}
pub(super) fn current_line(&self) -> &'a str {
let remaining = self.tracker.remaining();
let end = remaining.find('\n').unwrap_or(remaining.len());
&remaining[..end]
}
#[must_use]
fn at_next_section(&self) -> bool {
self.tracker.remaining().trim_start().starts_with('[')
}
fn skip_whitespace_and_comments(&mut self) {
loop {
self.tracker.skip_whitespace();
let remaining = self.tracker.remaining();
if remaining.is_empty() {
break;
}
if remaining.starts_with(';') || remaining.starts_with('#') {
self.tracker.skip_line();
continue;
}
if remaining.starts_with('\n') {
self.tracker.advance(1);
continue;
}
break;
}
}
#[must_use]
pub fn issues(self) -> Vec<ParseIssue> {
self.issues
}
#[must_use]
pub const fn format(&self) -> Option<&Vec<&'a str>> {
self.format.as_ref()
}
}