1use std::collections::BTreeMap;
2
3use winnow::Parser;
4
5use crate::ast::PgnPacket;
6use crate::errors::ParseError;
7use crate::lexer::{header_line, field_line};
8
9const MAX_PACKET_BYTES: usize = 1_000_000;
10const MAX_FIELDS: usize = 100;
11const MAX_FIELD_LENGTH: usize = 10_000;
12
13pub fn parse_packet(input: &str) -> Result<PgnPacket, ParseError> {
14 let input = input.strip_prefix('\u{FEFF}').unwrap_or(input);
16
17 if input.len() > MAX_PACKET_BYTES {
18 return Err(ParseError::PacketTooLarge(input.len()));
19 }
20
21 let input = input.trim();
22 let mut lines: Vec<&str> = input.lines().collect();
23
24 while lines.last().is_some_and(|l| l.trim().is_empty()) {
25 lines.pop();
26 }
27
28 if lines.is_empty() {
29 return Err(ParseError::MissingHeader);
30 }
31
32 let mut header_idx = None;
33 for (i, line) in lines.iter().enumerate() {
34 let trimmed = line.trim();
35 if trimmed.is_empty() || trimmed.starts_with('#') {
36 continue;
37 }
38 header_idx = Some(i);
39 break;
40 }
41
42 let header_idx = header_idx.ok_or(ParseError::MissingHeader)?;
43 let header_line_str = lines[header_idx].trim();
44
45 if !header_line_str.starts_with('@') {
46 return Err(ParseError::MissingHeader);
47 }
48
49 let mut hdr_input = header_line_str;
50 let (directive, run_id) = header_line
51 .parse_next(&mut hdr_input)
52 .map_err(|_| ParseError::MissingHeader)?;
53
54 if run_id.is_empty() {
55 return Err(ParseError::EmptyRunId);
56 }
57
58 let mut fields = BTreeMap::new();
59
60 for (line_idx, line) in lines[header_idx + 1..].iter().enumerate() {
61 let trimmed = line.trim();
62 if trimmed.is_empty() || trimmed.starts_with('#') {
63 continue;
64 }
65
66 if fields.len() >= MAX_FIELDS {
67 return Err(ParseError::TooManyFields(MAX_FIELDS));
68 }
69
70 let mut field_input = trimmed;
71 let (name, value) = field_line
72 .parse_next(&mut field_input)
73 .map_err(|_| ParseError::InvalidField(header_idx + 1 + line_idx))?;
74
75 if name.len() > MAX_FIELD_LENGTH {
76 return Err(ParseError::FieldTooLong(header_idx + 1 + line_idx));
77 }
78
79 if fields.contains_key(&name) {
80 return Err(ParseError::DuplicateField(name));
81 }
82
83 match &value {
84 crate::ast::FieldValue::Scalar(s) => {
85 if s.len() > MAX_FIELD_LENGTH {
86 return Err(ParseError::FieldTooLong(header_idx + 1 + line_idx));
87 }
88 }
89 crate::ast::FieldValue::List(items) => {
90 for item in items {
91 if item.len() > MAX_FIELD_LENGTH {
92 return Err(ParseError::FieldTooLong(header_idx + 1 + line_idx));
93 }
94 }
95 }
96 }
97
98 fields.insert(name, value);
99 }
100
101 Ok(PgnPacket {
102 directive,
103 run_id,
104 fields,
105 })
106}