ass_core/parser/sections/events/
parser.rs1use super::EventsParser;
8use crate::parser::{
9 ast::Section, errors::ParseIssue, position_tracker::PositionTracker,
10 sections::SectionParseResult, ParseResult,
11};
12use alloc::vec::Vec;
13
14impl<'a> EventsParser<'a> {
15 #[must_use]
23 #[allow(clippy::missing_const_for_fn)] pub fn new(source: &'a str, start_position: usize, start_line: usize) -> Self {
25 Self {
26 tracker: PositionTracker::new_at(
27 source,
28 start_position,
29 u32::try_from(start_line).unwrap_or(u32::MAX),
30 1,
31 ),
32 issues: Vec::new(),
33 format: None,
34 }
35 }
36
37 #[must_use]
39 pub fn with_format(
40 source: &'a str,
41 format: &[&'a str],
42 start_position: usize,
43 start_line: u32,
44 ) -> Self {
45 Self {
46 tracker: PositionTracker::new_at(source, start_position, start_line, 1),
47 issues: Vec::new(),
48 format: Some(format.to_vec()),
49 }
50 }
51
52 pub fn parse(mut self) -> ParseResult<SectionParseResult<'a>> {
66 let mut events = Vec::new();
67
68 while !self.tracker.is_at_end() && !self.at_next_section() {
69 self.skip_whitespace_and_comments();
70
71 if self.tracker.is_at_end() || self.at_next_section() {
72 break;
73 }
74
75 let line_start = self.tracker.checkpoint();
76 let line = self.current_line().trim();
77
78 if line.is_empty() {
79 self.tracker.skip_line();
80 continue;
81 }
82
83 if line.starts_with("Format:") {
84 self.parse_format_line(line);
85 } else if let Some(event) = self.parse_event_line_internal(line, &line_start) {
86 events.push(event);
87 }
88
89 self.tracker.skip_line();
90 }
91
92 Ok((
93 Section::Events(events),
94 self.format,
95 self.issues,
96 self.tracker.offset(),
97 self.tracker.line() as usize,
98 ))
99 }
100
101 fn parse_format_line(&mut self, line: &'a str) {
103 if let Some(format_data) = line.strip_prefix("Format:") {
104 let fields: Vec<&'a str> = format_data.split(',').map(str::trim).collect();
105 self.format = Some(fields);
106 }
107 }
108
109 pub(super) fn current_line(&self) -> &'a str {
111 let remaining = self.tracker.remaining();
112 let end = remaining.find('\n').unwrap_or(remaining.len());
113 &remaining[..end]
114 }
115
116 #[must_use]
118 fn at_next_section(&self) -> bool {
119 self.tracker.remaining().trim_start().starts_with('[')
120 }
121
122 fn skip_whitespace_and_comments(&mut self) {
124 loop {
125 self.tracker.skip_whitespace();
126
127 let remaining = self.tracker.remaining();
128 if remaining.is_empty() {
129 break;
130 }
131
132 if remaining.starts_with(';') || remaining.starts_with('#') {
133 self.tracker.skip_line();
134 continue;
135 }
136
137 if remaining.starts_with('\n') {
139 self.tracker.advance(1);
140 continue;
141 }
142
143 break;
144 }
145 }
146
147 #[must_use]
149 pub fn issues(self) -> Vec<ParseIssue> {
150 self.issues
151 }
152
153 #[must_use]
155 pub const fn format(&self) -> Option<&Vec<&'a str>> {
156 self.format.as_ref()
157 }
158}