ass_core/parser/sections/styles/
parser.rs1use super::StylesParser;
7use crate::parser::{
8 ast::Section, position_tracker::PositionTracker, sections::SectionParseResult, ParseResult,
9};
10use alloc::{vec, vec::Vec};
11
12impl<'a> StylesParser<'a> {
13 #[must_use]
21 #[allow(clippy::missing_const_for_fn)] pub fn new(source: &'a str, start_position: usize, start_line: usize) -> Self {
23 Self {
24 tracker: PositionTracker::new_at(
25 source,
26 start_position,
27 u32::try_from(start_line).unwrap_or(u32::MAX),
28 1,
29 ),
30 issues: Vec::new(),
31 format: None,
32 }
33 }
34
35 #[must_use]
37 pub fn with_format(
38 source: &'a str,
39 format: &[&'a str],
40 start_position: usize,
41 start_line: u32,
42 ) -> Self {
43 Self {
44 tracker: PositionTracker::new_at(source, start_position, start_line, 1),
45 issues: Vec::new(),
46 format: Some(format.to_vec()),
47 }
48 }
49
50 pub fn parse(mut self) -> ParseResult<SectionParseResult<'a>> {
64 let mut styles = Vec::new();
65
66 while !self.tracker.is_at_end() && !self.at_next_section() {
67 self.skip_whitespace_and_comments();
68
69 if self.tracker.is_at_end() || self.at_next_section() {
70 break;
71 }
72
73 let line_start = self.tracker.checkpoint();
74 let line = self.current_line().trim();
75
76 if line.is_empty() {
77 self.tracker.skip_line();
78 continue;
79 }
80
81 if line.starts_with("Format:") {
82 self.parse_format_line(line);
83 } else if line.starts_with("Style:") {
84 if let Some(style_data) = line.strip_prefix("Style:") {
85 if let Some(style) =
86 self.parse_style_line_internal(style_data.trim(), &line_start)
87 {
88 styles.push(style);
89 }
90 }
91 }
92
93 self.tracker.skip_line();
94 }
95
96 let format_to_return = if self.format.is_none() && !styles.is_empty() {
98 Some(vec![
99 "Name",
100 "Fontname",
101 "Fontsize",
102 "PrimaryColour",
103 "SecondaryColour",
104 "OutlineColour",
105 "BackColour",
106 "Bold",
107 "Italic",
108 "Underline",
109 "StrikeOut",
110 "ScaleX",
111 "ScaleY",
112 "Spacing",
113 "Angle",
114 "BorderStyle",
115 "Outline",
116 "Shadow",
117 "Alignment",
118 "MarginL",
119 "MarginR",
120 "MarginV",
121 "Encoding",
122 ])
123 } else {
124 self.format
125 };
126
127 Ok((
128 Section::Styles(styles),
129 format_to_return,
130 self.issues,
131 self.tracker.offset(),
132 self.tracker.line() as usize,
133 ))
134 }
135
136 fn parse_format_line(&mut self, line: &'a str) {
138 if let Some(format_data) = line.strip_prefix("Format:") {
139 let fields: Vec<&'a str> = format_data.split(',').map(str::trim).collect();
140 self.format = Some(fields);
141 }
142 }
143
144 pub(super) fn current_line(&self) -> &'a str {
146 let remaining = self.tracker.remaining();
147 let end = remaining.find('\n').unwrap_or(remaining.len());
148 &remaining[..end]
149 }
150
151 fn at_next_section(&self) -> bool {
153 self.tracker.remaining().trim_start().starts_with('[')
154 }
155
156 fn skip_whitespace_and_comments(&mut self) {
158 loop {
159 self.tracker.skip_whitespace();
160
161 let remaining = self.tracker.remaining();
162 if remaining.is_empty() {
163 break;
164 }
165
166 if remaining.starts_with(';') || remaining.starts_with('#') {
167 self.tracker.skip_line();
168 continue;
169 }
170
171 if remaining.starts_with('\n') {
173 self.tracker.advance(1);
174 continue;
175 }
176
177 break;
178 }
179 }
180}