ass_core/parser/streaming/
delta.rs1use crate::parser::ast::Section;
7use alloc::{string::String, vec::Vec};
8
9#[derive(Debug, Clone)]
46pub enum ParseDelta<'a> {
47 AddSection(Section<'a>),
52
53 UpdateSection(usize, Section<'a>),
58
59 RemoveSection(usize),
64
65 ParseIssue(String),
70}
71
72impl<'a> ParseDelta<'a> {
73 #[must_use]
75 pub const fn add_section(section: Section<'a>) -> Self {
76 Self::AddSection(section)
77 }
78
79 #[must_use]
81 pub const fn update_section(index: usize, section: Section<'a>) -> Self {
82 Self::UpdateSection(index, section)
83 }
84
85 #[must_use]
87 pub const fn remove_section(index: usize) -> Self {
88 Self::RemoveSection(index)
89 }
90
91 #[must_use]
93 pub const fn parse_issue(message: String) -> Self {
94 Self::ParseIssue(message)
95 }
96
97 #[must_use]
99 pub const fn is_error(&self) -> bool {
100 matches!(self, Self::ParseIssue(_))
101 }
102
103 #[must_use]
105 pub const fn is_structural(&self) -> bool {
106 matches!(
107 self,
108 Self::AddSection(_) | Self::UpdateSection(_, _) | Self::RemoveSection(_)
109 )
110 }
111
112 #[must_use]
114 pub const fn section(&self) -> Option<&Section<'a>> {
115 match self {
116 Self::AddSection(section) | Self::UpdateSection(_, section) => Some(section),
117 _ => None,
118 }
119 }
120}
121
122#[derive(Debug, Clone)]
127pub struct DeltaBatch<'a> {
128 deltas: Vec<ParseDelta<'a>>,
130}
131
132impl<'a> DeltaBatch<'a> {
133 #[must_use]
135 pub const fn new() -> Self {
136 Self { deltas: Vec::new() }
137 }
138
139 #[must_use]
141 pub const fn from_deltas(deltas: Vec<ParseDelta<'a>>) -> Self {
142 Self { deltas }
143 }
144
145 pub fn push(&mut self, delta: ParseDelta<'a>) {
147 self.deltas.push(delta);
148 }
149
150 pub fn extend(&mut self, other_deltas: impl IntoIterator<Item = ParseDelta<'a>>) {
152 self.deltas.extend(other_deltas);
153 }
154
155 #[must_use]
157 #[allow(clippy::missing_const_for_fn)]
158 pub fn deltas(&self) -> &[ParseDelta<'a>] {
159 &self.deltas
160 }
161
162 #[must_use]
164 pub fn into_deltas(self) -> Vec<ParseDelta<'a>> {
165 self.deltas
166 }
167
168 #[must_use]
170 pub fn is_empty(&self) -> bool {
171 self.deltas.is_empty()
172 }
173
174 #[must_use]
176 pub fn len(&self) -> usize {
177 self.deltas.len()
178 }
179
180 #[must_use]
182 pub fn filter<F>(&self, predicate: F) -> Self
183 where
184 F: Fn(&ParseDelta<'a>) -> bool,
185 {
186 let filtered = self
187 .deltas
188 .iter()
189 .filter(|d| predicate(d))
190 .cloned()
191 .collect();
192 DeltaBatch::from_deltas(filtered)
193 }
194
195 #[must_use]
197 pub fn structural_only(&self) -> Self {
198 self.filter(ParseDelta::is_structural)
199 }
200
201 #[must_use]
203 pub fn errors_only(&self) -> Self {
204 self.filter(ParseDelta::is_error)
205 }
206
207 pub fn has_errors(&self) -> bool {
209 self.deltas.iter().any(ParseDelta::is_error)
210 }
211}
212
213impl Default for DeltaBatch<'_> {
214 fn default() -> Self {
215 Self::new()
216 }
217}
218
219impl<'a> FromIterator<ParseDelta<'a>> for DeltaBatch<'a> {
220 fn from_iter<T: IntoIterator<Item = ParseDelta<'a>>>(iter: T) -> Self {
221 Self::from_deltas(iter.into_iter().collect())
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use crate::parser::ast::{ScriptInfo, Span};
229 #[cfg(not(feature = "std"))]
230 use alloc::{format, string::ToString, vec};
231
232 #[test]
233 fn delta_creation() {
234 let section = Section::ScriptInfo(ScriptInfo {
235 fields: vec![],
236 span: Span::new(0, 0, 0, 0),
237 });
238 let delta = ParseDelta::add_section(section);
239 assert!(matches!(delta, ParseDelta::AddSection(_)));
240 assert!(!delta.is_error());
241 assert!(delta.is_structural());
242 }
243
244 #[test]
245 fn delta_properties() {
246 let remove_delta = ParseDelta::remove_section(5);
247 assert!(remove_delta.is_structural());
248 assert!(!remove_delta.is_error());
249 assert_eq!(remove_delta.section(), None);
250
251 let error_delta = ParseDelta::parse_issue("Test error".to_string());
252 assert!(!error_delta.is_structural());
253 assert!(error_delta.is_error());
254 }
255
256 #[test]
257 fn delta_batch_operations() {
258 let mut batch = DeltaBatch::new();
259 assert!(batch.is_empty());
260 assert_eq!(batch.len(), 0);
261
262 let section = Section::ScriptInfo(ScriptInfo {
263 fields: vec![],
264 span: Span::new(0, 0, 0, 0),
265 });
266 batch.push(ParseDelta::add_section(section));
267 batch.push(ParseDelta::parse_issue("Warning".to_string()));
268
269 assert!(!batch.is_empty());
270 assert_eq!(batch.len(), 2);
271 assert!(batch.has_errors());
272
273 let structural = batch.structural_only();
274 assert_eq!(structural.len(), 1);
275 assert!(!structural.has_errors());
276
277 let errors = batch.errors_only();
278 assert_eq!(errors.len(), 1);
279 assert!(errors.has_errors());
280 }
281
282 #[test]
283 fn batch_from_iterator() {
284 let deltas = vec![
285 ParseDelta::remove_section(0),
286 ParseDelta::parse_issue("Error".to_string()),
287 ];
288
289 let batch: DeltaBatch = deltas.into_iter().collect();
290 assert_eq!(batch.len(), 2);
291 }
292
293 #[test]
294 fn delta_update_section() {
295 let section = Section::ScriptInfo(ScriptInfo {
296 fields: vec![],
297 span: Span::new(0, 0, 0, 0),
298 });
299 let delta = ParseDelta::update_section(3, section);
300
301 assert!(matches!(delta, ParseDelta::UpdateSection(3, _)));
302 assert!(!delta.is_error());
303 assert!(delta.is_structural());
304 assert!(delta.section().is_some());
305 }
306
307 #[test]
308 fn delta_section_getter() {
309 let section = Section::ScriptInfo(ScriptInfo {
310 fields: vec![],
311 span: Span::new(0, 0, 0, 0),
312 });
313
314 let add_delta = ParseDelta::add_section(section.clone());
315 assert!(add_delta.section().is_some());
316
317 let update_delta = ParseDelta::update_section(0, section);
318 assert!(update_delta.section().is_some());
319
320 let remove_delta = ParseDelta::remove_section(1);
321 assert!(remove_delta.section().is_none());
322
323 let error_delta = ParseDelta::parse_issue("Test".to_string());
324 assert!(error_delta.section().is_none());
325 }
326
327 #[test]
328 fn delta_debug_formatting() {
329 let section = Section::ScriptInfo(ScriptInfo {
330 fields: vec![],
331 span: Span::new(0, 0, 0, 0),
332 });
333 let delta = ParseDelta::add_section(section);
334 let debug_str = format!("{delta:?}");
335 assert!(debug_str.contains("AddSection"));
336
337 let error_delta = ParseDelta::parse_issue("Error message".to_string());
338 let error_debug = format!("{error_delta:?}");
339 assert!(error_debug.contains("ParseIssue"));
340 assert!(error_debug.contains("Error message"));
341 }
342
343 #[test]
344 fn delta_clone() {
345 let section = Section::ScriptInfo(ScriptInfo {
346 fields: vec![],
347 span: Span::new(0, 0, 0, 0),
348 });
349 let delta = ParseDelta::add_section(section);
350 let cloned = delta.clone();
351
352 assert!(matches!(cloned, ParseDelta::AddSection(_)));
353 assert_eq!(delta.is_error(), cloned.is_error());
354 assert_eq!(delta.is_structural(), cloned.is_structural());
355 }
356
357 #[test]
358 fn delta_all_constructors() {
359 let section = Section::ScriptInfo(ScriptInfo {
360 fields: vec![],
361 span: Span::new(0, 0, 0, 0),
362 });
363
364 let add = ParseDelta::add_section(section.clone());
365 assert!(matches!(add, ParseDelta::AddSection(_)));
366
367 let update = ParseDelta::update_section(42, section);
368 assert!(matches!(update, ParseDelta::UpdateSection(42, _)));
369
370 let remove = ParseDelta::remove_section(99);
371 assert!(matches!(remove, ParseDelta::RemoveSection(99)));
372
373 let issue = ParseDelta::parse_issue("Test issue".to_string());
374 assert!(matches!(issue, ParseDelta::ParseIssue(_)));
375 }
376
377 #[test]
378 fn batch_default() {
379 let batch = DeltaBatch::default();
380 assert!(batch.is_empty());
381 assert_eq!(batch.len(), 0);
382 assert!(!batch.has_errors());
383 }
384
385 #[test]
386 fn batch_debug_and_clone() {
387 let section = Section::ScriptInfo(ScriptInfo {
388 fields: vec![],
389 span: Span::new(0, 0, 0, 0),
390 });
391 let mut batch = DeltaBatch::new();
392 batch.push(ParseDelta::add_section(section));
393
394 let debug_str = format!("{batch:?}");
395 assert!(debug_str.contains("DeltaBatch"));
396
397 let cloned = batch.clone();
398 assert_eq!(batch.len(), cloned.len());
399 assert_eq!(batch.is_empty(), cloned.is_empty());
400 }
401
402 #[test]
403 fn batch_extend_operations() {
404 let mut batch = DeltaBatch::new();
405 let section1 = Section::ScriptInfo(ScriptInfo {
406 fields: vec![],
407 span: Span::new(0, 0, 0, 0),
408 });
409 let section2 = Section::ScriptInfo(ScriptInfo {
410 fields: vec![],
411 span: Span::new(0, 0, 0, 0),
412 });
413
414 let deltas = vec![
415 ParseDelta::add_section(section1),
416 ParseDelta::update_section(0, section2),
417 ParseDelta::remove_section(1),
418 ];
419
420 batch.extend(deltas);
421 assert_eq!(batch.len(), 3);
422 assert!(!batch.has_errors());
423 }
424
425 #[test]
426 fn batch_from_deltas() {
427 let section = Section::ScriptInfo(ScriptInfo {
428 fields: vec![],
429 span: Span::new(0, 0, 0, 0),
430 });
431 let deltas = vec![
432 ParseDelta::add_section(section),
433 ParseDelta::parse_issue("Warning".to_string()),
434 ];
435
436 let batch = DeltaBatch::from_deltas(deltas);
437 assert_eq!(batch.len(), 2);
438 assert!(batch.has_errors());
439 }
440
441 #[test]
442 fn batch_into_deltas() {
443 let section = Section::ScriptInfo(ScriptInfo {
444 fields: vec![],
445 span: Span::new(0, 0, 0, 0),
446 });
447 let mut batch = DeltaBatch::new();
448 batch.push(ParseDelta::add_section(section));
449 batch.push(ParseDelta::remove_section(0));
450
451 let deltas = batch.into_deltas();
452 assert_eq!(deltas.len(), 2);
453 }
454
455 #[test]
456 fn batch_complex_filtering() {
457 let section1 = Section::ScriptInfo(ScriptInfo {
458 fields: vec![],
459 span: Span::new(0, 0, 0, 0),
460 });
461 let section2 = Section::ScriptInfo(ScriptInfo {
462 fields: vec![],
463 span: Span::new(0, 0, 0, 0),
464 });
465 let mut batch = DeltaBatch::new();
466
467 batch.push(ParseDelta::add_section(section1));
468 batch.push(ParseDelta::update_section(0, section2));
469 batch.push(ParseDelta::remove_section(1));
470 batch.push(ParseDelta::parse_issue("Error 1".to_string()));
471 batch.push(ParseDelta::parse_issue("Error 2".to_string()));
472
473 assert_eq!(batch.len(), 5);
474
475 let structural = batch.structural_only();
476 assert_eq!(structural.len(), 3);
477 assert!(!structural.has_errors());
478
479 let errors = batch.errors_only();
480 assert_eq!(errors.len(), 2);
481 assert!(errors.has_errors());
482
483 let only_adds = batch.filter(|delta| matches!(delta, ParseDelta::AddSection(_)));
485 assert_eq!(only_adds.len(), 1);
486 }
487
488 #[test]
489 fn batch_empty_operations() {
490 let batch = DeltaBatch::new();
491
492 let structural = batch.structural_only();
493 assert!(structural.is_empty());
494
495 let errors = batch.errors_only();
496 assert!(errors.is_empty());
497
498 assert!(!batch.has_errors());
499 assert_eq!(batch.deltas().len(), 0);
500 }
501
502 #[test]
503 fn delta_all_variants_coverage() {
504 let section = Section::ScriptInfo(ScriptInfo {
506 fields: vec![],
507 span: Span::new(0, 0, 0, 0),
508 });
509
510 let add = ParseDelta::AddSection(section.clone());
512 assert!(add.is_structural());
513 assert!(!add.is_error());
514 assert!(add.section().is_some());
515
516 let update = ParseDelta::UpdateSection(5, section);
518 assert!(update.is_structural());
519 assert!(!update.is_error());
520 assert!(update.section().is_some());
521
522 let remove = ParseDelta::RemoveSection(10);
524 assert!(remove.is_structural());
525 assert!(!remove.is_error());
526 assert!(remove.section().is_none());
527
528 let issue = ParseDelta::ParseIssue("Critical error".to_string());
530 assert!(!issue.is_structural());
531 assert!(issue.is_error());
532 assert!(issue.section().is_none());
533 }
534
535 #[test]
536 fn batch_iterator_trait() {
537 let section = Section::ScriptInfo(ScriptInfo {
538 fields: vec![],
539 span: Span::new(0, 0, 0, 0),
540 });
541 let deltas = [
542 ParseDelta::add_section(section),
543 ParseDelta::remove_section(0),
544 ParseDelta::parse_issue("Test".to_string()),
545 ];
546
547 let batch: DeltaBatch = deltas.into_iter().collect();
548 assert_eq!(batch.len(), 3);
549
550 let filtered_deltas = batch
552 .deltas()
553 .iter()
554 .filter(|&d| d.is_structural())
555 .cloned();
556 let filtered_batch: DeltaBatch = filtered_deltas.collect();
557 assert_eq!(filtered_batch.len(), 2);
558 }
559}