1use super::PcdHeader;
16use crate::error::{PcdError, Result};
17use std::io::BufRead;
18
19pub fn parse_header<R: BufRead>(reader: &mut R) -> Result<PcdHeader> {
20 let mut header = PcdHeader::default();
21 let mut line_num = 0;
22
23 header.viewpoint = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0];
25
26 loop {
27 let mut line = String::new();
28 let bytes_read = reader.read_line(&mut line)?;
29 if bytes_read == 0 {
30 return Err(PcdError::InvalidHeader {
31 line: line_num,
32 msg: "Unexpected EOF before DATA section".to_string(),
33 });
34 }
35 line_num += 1;
36
37 let trimmed = line.trim();
38 if trimmed.is_empty() || trimmed.starts_with('#') {
39 continue;
40 }
41
42 let parts: Vec<&str> = trimmed.split_whitespace().collect();
43 if parts.is_empty() {
44 continue;
45 }
46
47 match parts[0] {
48 "VERSION" => {
49 header.version = parts.get(1).map(|&s| s.to_string()).unwrap_or_default();
50 }
51 "FIELDS" => {
52 header.fields = parts[1..].iter().map(|&s| s.to_string()).collect();
53 }
54 "SIZE" => {
55 header.sizes = parse_vec(&parts[1..], line_num, "SIZE")?;
56 }
57 "TYPE" => {
58 let type_chars: Result<Vec<char>> = parts[1..]
59 .iter()
60 .map(|s| {
61 if s.len() != 1 {
62 return Err(PcdError::InvalidHeader {
63 line: line_num,
64 msg: format!("Invalid TYPE: {}", s),
65 });
66 }
67 Ok(s.chars().next().unwrap())
68 })
69 .collect();
70 header.types = type_chars?;
71 }
72 "COUNT" => {
73 header.counts = parse_vec(&parts[1..], line_num, "COUNT")?;
74 }
75 "WIDTH" => {
76 header.width = parse_single(parts.get(1), line_num, "WIDTH")?;
77 }
78 "HEIGHT" => {
79 header.height = parse_single(parts.get(1), line_num, "HEIGHT")?;
80 }
81 "VIEWPOINT" => {
82 let vp: Vec<f64> = parse_vec(&parts[1..], line_num, "VIEWPOINT")?;
83 if vp.len() == 7 {
84 header.viewpoint.copy_from_slice(&vp);
85 } else {
86 return Err(PcdError::InvalidHeader {
87 line: line_num,
88 msg: format!("VIEWPOINT expected 7 values, got {}", vp.len()),
89 });
90 }
91 }
92 "POINTS" => {
93 header.points = parse_single(parts.get(1), line_num, "POINTS")?;
94 }
95 "DATA" => {
96 let fmt = parts.get(1).ok_or_else(|| PcdError::InvalidHeader {
97 line: line_num,
98 msg: "Missing DATA format".to_string(),
99 })?;
100 header.data = fmt.parse()?;
101
102 if header.counts.is_empty() {
104 header.counts = vec![1; header.fields.len()];
105 }
106
107 validate_header(&header, line_num)?;
109
110 return Ok(header);
111 }
112 _ => {
113 }
116 }
117 }
118}
119
120fn parse_vec<T: std::str::FromStr>(parts: &[&str], line: usize, field: &str) -> Result<Vec<T>> {
121 parts
122 .iter()
123 .map(|s| {
124 s.parse::<T>().map_err(|_| PcdError::InvalidHeader {
125 line,
126 msg: format!("Invalid value for {}: {}", field, s),
127 })
128 })
129 .collect()
130}
131
132fn parse_single<T: std::str::FromStr>(part: Option<&&str>, line: usize, field: &str) -> Result<T> {
133 match part {
134 Some(s) => s.parse::<T>().map_err(|_| PcdError::InvalidHeader {
135 line,
136 msg: format!("Invalid token for {}: {}", field, s),
137 }),
138 None => Err(PcdError::InvalidHeader {
139 line,
140 msg: format!("Missing value for {}", field),
141 }),
142 }
143}
144
145fn validate_header(header: &PcdHeader, line: usize) -> Result<()> {
146 if header.fields.len() != header.sizes.len() {
147 return Err(PcdError::InvalidHeader {
148 line,
149 msg: format!(
150 "Mismatch in fields({}) and sizes({})",
151 header.fields.len(),
152 header.sizes.len()
153 ),
154 });
155 }
156 if header.fields.len() != header.types.len() {
157 return Err(PcdError::InvalidHeader {
158 line,
159 msg: format!(
160 "Mismatch in fields({}) and types({})",
161 header.fields.len(),
162 header.types.len()
163 ),
164 });
165 }
166 if header.counts.is_empty() {
168 return Err(PcdError::InvalidHeader {
175 line,
176 msg: format!("Counts vector empty but fields present (logic error in parser)"),
177 });
178 } else if header.counts.len() != header.fields.len() {
179 return Err(PcdError::InvalidHeader {
180 line,
181 msg: format!(
182 "Mismatch in fields({}) and counts({})",
183 header.fields.len(),
184 header.counts.len()
185 ),
186 });
187 }
188
189 Ok(())
190}