bye_pcd_rs/
utils.rs

1use crate::{
2    error::Error,
3    metas::{DataKind, FieldDef, PcdMeta, Schema, TypeKind, ValueKind, ViewPoint},
4    Result,
5};
6use std::{collections::HashSet, io::prelude::*};
7
8pub fn load_meta<R: BufRead>(reader: &mut R, line_count: &mut usize) -> Result<PcdMeta> {
9    let mut get_meta_line = |expect_entry: &str| -> Result<_> {
10        loop {
11            let mut line = String::new();
12            let read_size = reader.read_line(&mut line)?;
13            *line_count += 1;
14
15            if read_size == 0 {
16                return Err(Error::new_parse_error(*line_count, "Unexpected end of file").into());
17            }
18
19            let line_stripped = match line.split('#').next() {
20                Some("") => continue,
21                Some(remaining) => remaining,
22                None => continue,
23            };
24
25            let tokens: Vec<String> = line_stripped
26                .split_ascii_whitespace()
27                .map(|s| s.to_owned())
28                .collect();
29
30            if tokens.is_empty() {
31                let desc = format!("Cannot parse empty line at line {}", *line_count + 1);
32                return Err(Error::new_parse_error(*line_count, &desc).into());
33            }
34
35            if tokens[0] != expect_entry {
36                let desc = format!(
37                    "Expect {:?} entry, found {:?} at line {}",
38                    expect_entry,
39                    tokens[0],
40                    *line_count + 1
41                );
42                return Err(Error::new_parse_error(*line_count, &desc).into());
43            }
44
45            return Ok(tokens);
46        }
47    };
48
49    let meta_version = {
50        let tokens = get_meta_line("VERSION")?;
51        if tokens.len() == 2 {
52            match tokens[1].as_str() {
53                "0.7" => String::from("0.7"),
54                ".7" => String::from("0.7"),
55                _ => {
56                    let desc = format!(
57                        "Unsupported version {:?}. Supported versions are: 0.7",
58                        tokens[1]
59                    );
60                    return Err(Error::new_parse_error(*line_count, &desc).into());
61                }
62            }
63        } else {
64            return Err(
65                Error::new_parse_error(*line_count, "VERSION line is not understood").into(),
66            );
67        }
68    };
69
70    let meta_fields = {
71        let tokens = get_meta_line("FIELDS")?;
72        if tokens.len() == 1 {
73            return Err(
74                Error::new_parse_error(*line_count, "FIELDS line is not understood").into(),
75            );
76        }
77
78        let mut name_set = HashSet::new();
79        let mut field_names: Vec<String> = vec![];
80
81        for (idx, tk) in tokens[1..].iter().enumerate() {
82            let mut field = tk.clone();
83            // If this field is just an underscore, it was meant to be skipped. Label it as
84            // unknown_field_{idx}
85            if field == String::from("_") {
86                field = format!("unknown_field_{idx}");
87            }
88
89            if name_set.contains(&field.clone()) {
90                let desc = format!("field name {:?} is specified more than once", field);
91                return Err(Error::new_parse_error(*line_count, &desc).into());
92            }
93
94            name_set.insert(field.clone());
95            field_names.push(field.to_owned());
96        }
97
98        field_names
99    };
100
101    let meta_size = {
102        let tokens = get_meta_line("SIZE")?;
103        if tokens.len() == 1 {
104            return Err(Error::new_parse_error(*line_count, "SIZE line is not understood").into());
105        }
106
107        let mut sizes = vec![];
108        for tk in tokens[1..].iter() {
109            let size: u64 = tk.parse()?;
110            sizes.push(size);
111        }
112
113        sizes
114    };
115
116    let meta_type = {
117        let tokens = get_meta_line("TYPE")?;
118
119        if tokens.len() == 1 {
120            return Err(Error::new_parse_error(*line_count, "TYPE line is not understood").into());
121        }
122
123        let mut types = vec![];
124        for type_char in tokens[1..].iter() {
125            let type_ = match type_char.as_str() {
126                "I" => TypeKind::I,
127                "U" => TypeKind::U,
128                "F" => TypeKind::F,
129                _ => {
130                    let desc = format!("Invalid type character {:?} in TYPE line", type_char);
131                    return Err(Error::new_parse_error(*line_count, &desc).into());
132                }
133            };
134            types.push(type_);
135        }
136
137        types
138    };
139
140    let meta_count = {
141        let tokens = get_meta_line("COUNT")?;
142
143        if tokens.len() == 1 {
144            return Err(Error::new_parse_error(*line_count, "COUNT line is not understood").into());
145        }
146
147        let mut counts = vec![];
148        for tk in tokens[1..].iter() {
149            let count: u64 = tk.parse()?;
150            counts.push(count);
151        }
152
153        counts
154    };
155
156    let meta_width = {
157        let tokens = get_meta_line("WIDTH")?;
158
159        if tokens.len() != 2 {
160            return Err(Error::new_parse_error(*line_count, "WIDTH line is not understood").into());
161        }
162
163        let width: u64 = tokens[1].parse()?;
164        width
165    };
166
167    let meta_height = {
168        let tokens = get_meta_line("HEIGHT")?;
169        if tokens.len() != 2 {
170            return Err(
171                Error::new_parse_error(*line_count, "HEIGHT line is not understood").into(),
172            );
173        }
174
175        let height: u64 = tokens[1].parse()?;
176        height
177    };
178
179    let meta_viewpoint = {
180        let tokens = get_meta_line("VIEWPOINT")?;
181
182        if tokens.len() != 8 {
183            return Err(
184                Error::new_parse_error(*line_count, "VIEWPOINT line is not understood").into(),
185            );
186        }
187
188        let tx = tokens[1].parse()?;
189        let ty = tokens[2].parse()?;
190        let tz = tokens[3].parse()?;
191        let qw = tokens[4].parse()?;
192        let qx = tokens[5].parse()?;
193        let qy = tokens[6].parse()?;
194        let qz = tokens[7].parse()?;
195        ViewPoint {
196            tx,
197            ty,
198            tz,
199            qw,
200            qx,
201            qy,
202            qz,
203        }
204    };
205
206    let meta_points = {
207        let tokens = get_meta_line("POINTS")?;
208
209        if tokens.len() != 2 {
210            return Err(
211                Error::new_parse_error(*line_count, "POINTS line is not understood").into(),
212            );
213        }
214
215        let count: u64 = tokens[1].parse()?;
216        count
217    };
218
219    let meta_data = {
220        let tokens = get_meta_line("DATA")?;
221
222        if tokens.len() != 2 {
223            return Err(Error::new_parse_error(*line_count, "DATA line is not understood").into());
224        }
225
226        match tokens[1].as_str() {
227            "ascii" => DataKind::Ascii,
228            "binary" => DataKind::Binary,
229            _ => {
230                return Err(
231                    Error::new_parse_error(*line_count, "DATA line is not understood").into(),
232                );
233            }
234        }
235    };
236
237    // Check integrity
238    if meta_size.len() != meta_fields.len() {
239        return Err(
240            Error::new_parse_error(*line_count, "SIZE entry conflicts with FIELD entry").into(),
241        );
242    }
243
244    if meta_type.len() != meta_fields.len() {
245        return Err(
246            Error::new_parse_error(*line_count, "TYPE entry conflicts with FIELD entry").into(),
247        );
248    }
249
250    if meta_count.len() != meta_fields.len() {
251        return Err(
252            Error::new_parse_error(*line_count, "COUNT entry conflicts with FIELD entry").into(),
253        );
254    }
255
256    // Organize field type
257    let field_defs: Result<Schema> = {
258        meta_fields
259            .iter()
260            .zip(meta_type.iter())
261            .zip(meta_size.iter())
262            .zip(meta_count.iter())
263            .map(|(((name, type_), size), &count)| {
264                let kind = match (type_, size) {
265                    (TypeKind::U, 1) => ValueKind::U8,
266                    (TypeKind::U, 2) => ValueKind::U16,
267                    (TypeKind::U, 4) => ValueKind::U32,
268                    (TypeKind::I, 1) => ValueKind::I8,
269                    (TypeKind::I, 2) => ValueKind::I16,
270                    (TypeKind::I, 4) => ValueKind::I32,
271                    (TypeKind::F, 4) => ValueKind::F32,
272                    (TypeKind::F, 8) => ValueKind::F64,
273                    _ => {
274                        let desc =
275                            format!("Field type {:?} with size {} is not supported", type_, size);
276                        return Err(Error::new_parse_error(*line_count, &desc).into());
277                    }
278                };
279
280                let meta = FieldDef {
281                    name: name.to_owned(),
282                    kind,
283                    count,
284                };
285
286                Ok(meta)
287            })
288            .collect()
289    };
290
291    let meta = PcdMeta {
292        version: meta_version,
293        field_defs: field_defs?,
294        width: meta_width,
295        height: meta_height,
296        viewpoint: meta_viewpoint,
297        num_points: meta_points,
298        data: meta_data,
299    };
300
301    Ok(meta)
302}