pasture_io/ascii/
raw_reader.rs

1use anyhow::{bail, Context, Result};
2use byteorder::{LittleEndian, WriteBytesExt};
3use itertools::{EitherOrBoth::*, Itertools};
4use pasture_core::containers::BorrowedMutBuffer;
5use pasture_core::layout::attributes;
6use pasture_core::layout::PointAttributeDefinition;
7use pasture_core::layout::PointLayout;
8use pasture_core::meta::Metadata;
9use std::collections::HashSet;
10use std::io::{BufRead, Read};
11use std::iter::FromIterator;
12use std::str::FromStr;
13
14use super::AsciiMetadata;
15use super::PointDataType;
16use crate::base::PointReader;
17use pasture_core::containers::{UntypedPoint, UntypedPointBuffer};
18
19pub(crate) struct RawAsciiReader<T: Read + BufRead> {
20    reader: T,
21    metadata: AsciiMetadata,
22    delimiter: String,
23    point_layout: PointLayout,
24    parse_layout: Vec<PointDataType>,
25}
26impl<T: Read + BufRead> RawAsciiReader<T> {
27    pub fn from_read(read: T, format: &str, delimiter: &str) -> Result<Self> {
28        let parse_layout = PointDataType::get_parse_layout(format)?;
29        let layout = Self::get_point_layout_from_parse_layout(&parse_layout);
30        let metadata = AsciiMetadata;
31
32        Ok(Self {
33            reader: read,
34            metadata,
35            delimiter: delimiter.to_string(),
36            point_layout: layout,
37            parse_layout,
38        })
39    }
40
41    fn get_point_layout_from_parse_layout(parse_layout: &[PointDataType]) -> PointLayout {
42        let hashset = parse_layout
43            .iter()
44            .filter(|data_type| !matches!(data_type, PointDataType::Skip))
45            .map(|data_type| match data_type {
46                PointDataType::CoordinateX
47                | PointDataType::CoordinateY
48                | PointDataType::CoordinateZ => attributes::POSITION_3D,
49                PointDataType::Intensity => attributes::INTENSITY,
50                PointDataType::ReturnNumber => attributes::RETURN_NUMBER,
51                PointDataType::NumberOfReturns => attributes::NUMBER_OF_RETURNS,
52                PointDataType::Classification => attributes::CLASSIFICATION,
53                PointDataType::UserData => attributes::USER_DATA,
54                PointDataType::ColorR | PointDataType::ColorG | PointDataType::ColorB => {
55                    attributes::COLOR_RGB
56                }
57                PointDataType::GpsTime => attributes::GPS_TIME,
58                PointDataType::PointSourceID => attributes::POINT_SOURCE_ID,
59                PointDataType::EdgeOfFlightLine => attributes::EDGE_OF_FLIGHT_LINE,
60                PointDataType::ScanDirectionFlag => attributes::SCAN_DIRECTION_FLAG,
61                PointDataType::ScanAngleRank => attributes::SCAN_ANGLE_RANK,
62                PointDataType::Nir => attributes::NIR,
63                PointDataType::Skip => panic!("Skip should be filtered"),
64            })
65            .collect::<HashSet<_>>();
66        PointLayout::from_attributes(&Vec::from_iter(hashset))
67    }
68
69    fn parse_point(
70        point: &mut UntypedPointBuffer,
71        line: &str,
72        delimiter: &str,
73        parse_layout: &[PointDataType],
74    ) -> Result<()> {
75        for pair in line.split(delimiter).zip_longest(parse_layout) {
76            match pair {
77                Both(value_str, data_type) => match data_type {
78                    PointDataType::CoordinateX => {
79                        Self::parse_to_point_f64(point, &attributes::POSITION_3D, 0, value_str)
80                            .with_context(|| generate_parse_error(data_type, 'x'))?;
81                    }
82                    PointDataType::CoordinateY => {
83                        Self::parse_to_point_f64(
84                            point,
85                            &attributes::POSITION_3D,
86                            std::mem::size_of::<f64>() as u64,
87                            value_str,
88                        )
89                        .with_context(|| generate_parse_error(data_type, 'y'))?;
90                    }
91                    PointDataType::CoordinateZ => {
92                        Self::parse_to_point_f64(
93                            point,
94                            &attributes::POSITION_3D,
95                            2 * std::mem::size_of::<f64>() as u64,
96                            value_str,
97                        )
98                        .with_context(|| generate_parse_error(data_type, 'z'))?;
99                    }
100                    PointDataType::Intensity => {
101                        Self::parse_to_point_u16(point, &attributes::INTENSITY, 0, value_str)
102                            .with_context(|| generate_parse_error(data_type, 'i'))?;
103                    }
104                    PointDataType::ReturnNumber => {
105                        Self::parse_to_point_u8(point, &attributes::RETURN_NUMBER, 0, value_str)
106                            .with_context(|| generate_parse_error(data_type, 'r'))?;
107                    }
108                    PointDataType::NumberOfReturns => {
109                        Self::parse_to_point_u8(
110                            point,
111                            &attributes::NUMBER_OF_RETURNS,
112                            0,
113                            value_str,
114                        )
115                        .with_context(|| generate_parse_error(data_type, 'n'))?;
116                    }
117                    PointDataType::Classification => {
118                        Self::parse_to_point_u8(point, &attributes::CLASSIFICATION, 0, value_str)
119                            .with_context(|| generate_parse_error(data_type, 'c'))?;
120                    }
121                    PointDataType::UserData => {
122                        Self::parse_to_point_u8(point, &attributes::USER_DATA, 0, value_str)
123                            .with_context(|| generate_parse_error(data_type, 'u'))?;
124                    }
125                    PointDataType::ColorR => {
126                        Self::parse_to_point_u16(point, &attributes::COLOR_RGB, 0, value_str)
127                            .with_context(|| generate_parse_error(data_type, 'R'))?;
128                    }
129                    PointDataType::ColorG => {
130                        Self::parse_to_point_u16(
131                            point,
132                            &attributes::COLOR_RGB,
133                            std::mem::size_of::<u16>() as u64,
134                            value_str,
135                        )
136                        .with_context(|| generate_parse_error(data_type, 'G'))?;
137                    }
138                    PointDataType::ColorB => {
139                        Self::parse_to_point_u16(
140                            point,
141                            &attributes::COLOR_RGB,
142                            2 * std::mem::size_of::<u16>() as u64,
143                            value_str,
144                        )
145                        .with_context(|| generate_parse_error(data_type, 'B'))?;
146                    }
147                    PointDataType::GpsTime => {
148                        Self::parse_to_point_f64(point, &attributes::GPS_TIME, 0, value_str)
149                            .with_context(|| generate_parse_error(data_type, 't'))?;
150                    }
151                    PointDataType::PointSourceID => {
152                        Self::parse_to_point_u16(point, &attributes::POINT_SOURCE_ID, 0, value_str)
153                            .with_context(|| generate_parse_error(data_type, 'p'))?;
154                    }
155                    PointDataType::EdgeOfFlightLine => {
156                        Self::parse_to_point_bool(
157                            point,
158                            &attributes::EDGE_OF_FLIGHT_LINE,
159                            0,
160                            value_str,
161                        )
162                        .with_context(|| generate_parse_error(data_type, 'e'))?;
163                    }
164                    PointDataType::ScanDirectionFlag => {
165                        Self::parse_to_point_bool(
166                            point,
167                            &attributes::SCAN_DIRECTION_FLAG,
168                            0,
169                            value_str,
170                        )
171                        .with_context(|| generate_parse_error(data_type, 'd'))?;
172                    }
173                    PointDataType::ScanAngleRank => {
174                        Self::parse_to_point_i8(point, &attributes::SCAN_ANGLE_RANK, 0, value_str)
175                            .with_context(|| generate_parse_error(data_type, 'a'))?;
176                    }
177                    PointDataType::Nir => {
178                        Self::parse_to_point_u16(point, &attributes::NIR, 0, value_str)
179                            .with_context(|| generate_parse_error(data_type, 'I'))?;
180                    }
181                    PointDataType::Skip => {}
182                },
183                Left(_) => continue,
184                Right(_) => {
185                    bail!("Input format string expected more items in the line. Found End-of-Line.")
186                }
187            }
188        }
189        Ok(())
190    }
191
192    fn parse_to_point_i8(
193        point: &mut UntypedPointBuffer,
194        attribute: &PointAttributeDefinition,
195        offset: u64,
196        value_str: &str,
197    ) -> Result<()> {
198        let data = Self::parse_string::<i8>(value_str)?;
199        let attribute_offset = point.get_layout().offset_of(attribute);
200        if let Some(attribute_offset) = attribute_offset {
201            let mut cursor = point.get_cursor();
202            cursor.set_position(attribute_offset + offset);
203            cursor.write_i8(data)?;
204        }
205        Ok(())
206    }
207
208    fn parse_to_point_bool(
209        point: &mut UntypedPointBuffer,
210        attribute: &PointAttributeDefinition,
211        offset: u64,
212        value_str: &str,
213    ) -> Result<()> {
214        let data = Self::parse_string::<u8>(value_str)?;
215        if !(data == 0 || data == 1) {
216            bail!("ParseError expected bool found '{}'.", data);
217        }
218        let attribute_offset = point.get_layout().offset_of(attribute);
219        if let Some(attribute_offset) = attribute_offset {
220            let mut cursor = point.get_cursor();
221            cursor.set_position(attribute_offset + offset);
222            cursor.write_u8(data)?;
223        }
224        Ok(())
225    }
226
227    fn parse_to_point_u8(
228        point: &mut UntypedPointBuffer,
229        attribute: &PointAttributeDefinition,
230        offset: u64,
231        value_str: &str,
232    ) -> Result<()> {
233        let data = Self::parse_string::<u8>(value_str)?;
234        let attribute_offset = point.get_layout().offset_of(attribute);
235        if let Some(attribute_offset) = attribute_offset {
236            let mut cursor = point.get_cursor();
237            cursor.set_position(attribute_offset + offset);
238            cursor.write_u8(data)?;
239        }
240        Ok(())
241    }
242
243    fn parse_to_point_u16(
244        point: &mut UntypedPointBuffer,
245        attribute: &PointAttributeDefinition,
246        offset: u64,
247        value_str: &str,
248    ) -> Result<()> {
249        let data = Self::parse_string::<u16>(value_str)?;
250        let attribute_offset = point.get_layout().offset_of(attribute);
251        if let Some(attribute_offset) = attribute_offset {
252            let mut cursor = point.get_cursor();
253            cursor.set_position(attribute_offset + offset);
254            cursor.write_u16::<LittleEndian>(data)?;
255        }
256        Ok(())
257    }
258
259    fn parse_to_point_f64(
260        point: &mut UntypedPointBuffer,
261        attribute: &PointAttributeDefinition,
262        offset: u64,
263        value_str: &str,
264    ) -> Result<()> {
265        let data = Self::parse_string::<f64>(value_str)?;
266        let attribute_offset = point.get_layout().offset_of(attribute);
267        if let Some(attribute_offset) = attribute_offset {
268            let mut cursor = point.get_cursor();
269            cursor.set_position(attribute_offset + offset);
270            cursor.write_f64::<LittleEndian>(data)?;
271        }
272        Ok(())
273    }
274
275    fn parse_string<V: FromStr>(value_str: &str) -> Result<V, anyhow::Error> {
276        value_str.parse::<V>().map_err(|_| {
277            anyhow::anyhow!(
278                "ParseError expected {} found '{}'.",
279                std::any::type_name::<V>(),
280                value_str
281            )
282        })
283    }
284}
285
286fn generate_parse_error(datatype: &PointDataType, character: char) -> String {
287    format!(
288        "ParseError at parsing {} for format literal '{}'.",
289        datatype, character
290    )
291}
292
293impl<T: Read + BufRead> PointReader for RawAsciiReader<T> {
294    fn read_into<'a, 'b, B: BorrowedMutBuffer<'a>>(
295        &mut self,
296        point_buffer: &'b mut B,
297        count: usize,
298    ) -> Result<usize>
299    where
300        'a: 'b,
301    {
302        let layout = point_buffer.point_layout().clone();
303        let mut temp_point = UntypedPointBuffer::new(&layout);
304        //read line by line
305        for (index, line) in (&mut self.reader).lines().take(count).enumerate() {
306            let line = line?;
307            //parse the line in an untypedpoint
308            Self::parse_point(&mut temp_point, &line, &self.delimiter, &self.parse_layout)
309                .with_context(|| format!("ReadError in line {}.", index))?;
310            //put it in the buffer
311            unsafe {
312                point_buffer.set_point(index, temp_point.get_buffer());
313            }
314        }
315        Ok(count)
316    }
317
318    fn get_default_point_layout(&self) -> &PointLayout {
319        &self.point_layout
320    }
321
322    fn get_metadata(&self) -> &dyn Metadata {
323        &self.metadata
324    }
325}
326
327// Ascii Tests
328//  - Reading
329//      - `read` has to be correct.
330//      - `read_into` has to be correct for a buffer with the same layout.
331//      - `read_into` has to be correct for a buffer with a different layout.
332//  - Errors
333//      - Unrecognized format literal
334//      - To many format literals
335//      - Float parsing error
336//      - Integer parsing error
337//      - Bool parsing error
338#[cfg(test)]
339mod tests {
340    use super::*;
341    use crate::ascii::{
342        get_test_file_path, test_data_classifications, test_data_colors,
343        test_data_edge_of_flight_lines, test_data_gps_times, test_data_intensities, test_data_nirs,
344        test_data_number_of_returns, test_data_point_source_ids, test_data_positions,
345        test_data_return_numbers, test_data_scan_angle_ranks, test_data_scan_direction_flags,
346        test_data_user_data,
347    };
348    use anyhow::Result;
349    use pasture_core::containers::{
350        BorrowedBufferExt, MakeBufferFromLayout, OwningBuffer, VectorBuffer,
351    };
352    use pasture_core::layout::{attributes, PointType};
353    use pasture_core::nalgebra::Vector3;
354    use pasture_derive::PointType;
355    use std::{fs::File, io::BufReader};
356
357    #[test]
358    fn test_read() -> Result<()> {
359        let path = get_test_file_path("10_points_ascii_all_attributes.txt");
360        let reader = BufReader::new(File::open(path)?);
361        let mut ascii_reader = RawAsciiReader::from_read(reader, "xyzirncuRGBtpedaI", ", ")?;
362        let interleaved_buffer = ascii_reader.read::<VectorBuffer>(10)?;
363
364        let positions = interleaved_buffer
365            .view_attribute::<Vector3<f64>>(&attributes::POSITION_3D)
366            .into_iter()
367            .collect::<Vec<_>>();
368        assert_eq!(test_data_positions(), positions);
369
370        let intensities = interleaved_buffer
371            .view_attribute::<u16>(&attributes::INTENSITY)
372            .into_iter()
373            .collect::<Vec<_>>();
374        assert_eq!(test_data_intensities(), intensities);
375
376        let return_numbers = interleaved_buffer
377            .view_attribute::<u8>(&attributes::RETURN_NUMBER)
378            .into_iter()
379            .collect::<Vec<_>>();
380        assert_eq!(test_data_return_numbers(), return_numbers);
381
382        let number_of_returns = interleaved_buffer
383            .view_attribute::<u8>(&attributes::NUMBER_OF_RETURNS)
384            .into_iter()
385            .collect::<Vec<_>>();
386        assert_eq!(test_data_number_of_returns(), number_of_returns);
387
388        let classifications = interleaved_buffer
389            .view_attribute::<u8>(&attributes::CLASSIFICATION)
390            .into_iter()
391            .collect::<Vec<_>>();
392        assert_eq!(test_data_classifications(), classifications);
393
394        let user_datas = interleaved_buffer
395            .view_attribute::<u8>(&attributes::USER_DATA)
396            .into_iter()
397            .collect::<Vec<_>>();
398        assert_eq!(test_data_user_data(), user_datas);
399
400        let colors = interleaved_buffer
401            .view_attribute::<Vector3<u16>>(&attributes::COLOR_RGB)
402            .into_iter()
403            .collect::<Vec<_>>();
404        assert_eq!(test_data_colors(), colors);
405
406        let gps_times = interleaved_buffer
407            .view_attribute::<f64>(&attributes::GPS_TIME)
408            .into_iter()
409            .collect::<Vec<_>>();
410        assert_eq!(test_data_gps_times(), gps_times);
411
412        let point_source_ids = interleaved_buffer
413            .view_attribute::<u16>(&attributes::POINT_SOURCE_ID)
414            .into_iter()
415            .collect::<Vec<_>>();
416        assert_eq!(test_data_point_source_ids(), point_source_ids);
417
418        let edge_of_flight_lines = interleaved_buffer
419            .view_attribute::<u8>(&attributes::EDGE_OF_FLIGHT_LINE)
420            .into_iter()
421            .collect::<Vec<_>>();
422        assert_eq!(test_data_edge_of_flight_lines(), edge_of_flight_lines);
423
424        let scan_direction_flags = interleaved_buffer
425            .view_attribute::<u8>(&attributes::SCAN_DIRECTION_FLAG)
426            .into_iter()
427            .collect::<Vec<_>>();
428        assert_eq!(test_data_scan_direction_flags(), scan_direction_flags);
429
430        let scan_angle_ranks = interleaved_buffer
431            .view_attribute::<i8>(&attributes::SCAN_ANGLE_RANK)
432            .into_iter()
433            .collect::<Vec<_>>();
434        assert_eq!(test_data_scan_angle_ranks(), scan_angle_ranks);
435
436        let nirs = interleaved_buffer
437            .view_attribute::<u16>(&attributes::NIR)
438            .into_iter()
439            .collect::<Vec<_>>();
440        assert_eq!(test_data_nirs(), nirs);
441
442        Ok(())
443    }
444
445    #[repr(C, packed)]
446    #[derive(PointType, Debug, Copy, Clone, bytemuck::AnyBitPattern, bytemuck::NoUninit)]
447    struct TestPointAll {
448        #[pasture(BUILTIN_POSITION_3D)]
449        pub position: Vector3<f64>,
450        #[pasture(BUILTIN_INTENSITY)]
451        pub intensity: u16,
452        #[pasture(BUILTIN_RETURN_NUMBER)]
453        pub return_number: u8,
454        #[pasture(BUILTIN_NUMBER_OF_RETURNS)]
455        pub number_of_returns: u8,
456        #[pasture(BUILTIN_CLASSIFICATION)]
457        pub classification: u8,
458        #[pasture(BUILTIN_USER_DATA)]
459        pub user_data: u8,
460        #[pasture(BUILTIN_COLOR_RGB)]
461        pub color_rgb: Vector3<u16>,
462        #[pasture(BUILTIN_GPS_TIME)]
463        pub gps_time: f64,
464        #[pasture(BUILTIN_POINT_SOURCE_ID)]
465        pub point_source_id: u16,
466        #[pasture(BUILTIN_EDGE_OF_FLIGHT_LINE)]
467        pub edge_of_flight_line: u8,
468        #[pasture(BUILTIN_SCAN_DIRECTION_FLAG)]
469        pub scan_direction_flag: u8,
470        #[pasture(BUILTIN_SCAN_ANGLE_RANK)]
471        pub scan_angle_rank: i8,
472        #[pasture(BUILTIN_NIR)]
473        pub nir: u16,
474    }
475
476    #[test]
477    fn test_read_into_same_layout() -> Result<()> {
478        let path = get_test_file_path("10_points_ascii_all_attributes.txt");
479        let reader = BufReader::new(File::open(path)?);
480        let mut ascii_reader = RawAsciiReader::from_read(reader, "xyzirncuRGBtpedaI", ", ")?;
481        let mut buffer = VectorBuffer::new_from_layout(TestPointAll::layout());
482        buffer.resize(10);
483        ascii_reader.read_into(&mut buffer, 10)?;
484
485        let positions = buffer
486            .view_attribute::<Vector3<f64>>(&attributes::POSITION_3D)
487            .into_iter()
488            .collect::<Vec<_>>();
489        assert_eq!(test_data_positions(), positions);
490
491        let intensities = buffer
492            .view_attribute::<u16>(&attributes::INTENSITY)
493            .into_iter()
494            .collect::<Vec<_>>();
495        assert_eq!(test_data_intensities(), intensities);
496
497        let return_numbers = buffer
498            .view_attribute::<u8>(&attributes::RETURN_NUMBER)
499            .into_iter()
500            .collect::<Vec<_>>();
501        assert_eq!(test_data_return_numbers(), return_numbers);
502
503        let number_of_returns = buffer
504            .view_attribute::<u8>(&attributes::NUMBER_OF_RETURNS)
505            .into_iter()
506            .collect::<Vec<_>>();
507        assert_eq!(test_data_number_of_returns(), number_of_returns);
508
509        let classifications = buffer
510            .view_attribute::<u8>(&attributes::CLASSIFICATION)
511            .into_iter()
512            .collect::<Vec<_>>();
513        assert_eq!(test_data_classifications(), classifications);
514
515        let user_datas = buffer
516            .view_attribute::<u8>(&attributes::USER_DATA)
517            .into_iter()
518            .collect::<Vec<_>>();
519        assert_eq!(test_data_user_data(), user_datas);
520
521        let colors = buffer
522            .view_attribute::<Vector3<u16>>(&attributes::COLOR_RGB)
523            .into_iter()
524            .collect::<Vec<_>>();
525        assert_eq!(test_data_colors(), colors);
526
527        let gps_times = buffer
528            .view_attribute::<f64>(&attributes::GPS_TIME)
529            .into_iter()
530            .collect::<Vec<_>>();
531        assert_eq!(test_data_gps_times(), gps_times);
532
533        let point_source_ids = buffer
534            .view_attribute::<u16>(&attributes::POINT_SOURCE_ID)
535            .into_iter()
536            .collect::<Vec<_>>();
537        assert_eq!(test_data_point_source_ids(), point_source_ids);
538
539        let edge_of_flight_lines = buffer
540            .view_attribute::<u8>(&attributes::EDGE_OF_FLIGHT_LINE)
541            .into_iter()
542            .collect::<Vec<_>>();
543        assert_eq!(test_data_edge_of_flight_lines(), edge_of_flight_lines);
544
545        let scan_direction_flags = buffer
546            .view_attribute::<u8>(&attributes::SCAN_DIRECTION_FLAG)
547            .into_iter()
548            .collect::<Vec<_>>();
549        assert_eq!(test_data_scan_direction_flags(), scan_direction_flags);
550
551        let scan_angle_ranks = buffer
552            .view_attribute::<i8>(&attributes::SCAN_ANGLE_RANK)
553            .into_iter()
554            .collect::<Vec<_>>();
555        assert_eq!(test_data_scan_angle_ranks(), scan_angle_ranks);
556
557        let nirs = buffer
558            .view_attribute::<u16>(&attributes::NIR)
559            .into_iter()
560            .collect::<Vec<_>>();
561        assert_eq!(test_data_nirs(), nirs);
562        Ok(())
563    }
564
565    #[repr(C, packed)]
566    #[derive(PointType, Debug, Copy, Clone, bytemuck::AnyBitPattern, bytemuck::NoUninit)]
567    struct TestPointDifferent {
568        #[pasture(BUILTIN_POSITION_3D)]
569        pub position: Vector3<f64>,
570        #[pasture(BUILTIN_USER_DATA)]
571        pub user_data: u16,
572        #[pasture(BUILTIN_CLASSIFICATION)]
573        pub classification: u16,
574    }
575
576    #[test]
577    fn test_read_into_different_layout() -> Result<()> {
578        let path = get_test_file_path("10_points_ascii.txt");
579        let reader = BufReader::new(File::open(path)?);
580        let mut ascii_reader = RawAsciiReader::from_read(reader, "xyzieRGB", ", ")?;
581        let mut buffer = VectorBuffer::new_from_layout(TestPointDifferent::layout());
582        buffer.resize(10);
583        ascii_reader.read_into(&mut buffer, 10)?;
584
585        let position_expected = test_data_positions();
586        for (index, point) in buffer.view::<TestPointDifferent>().into_iter().enumerate() {
587            let position = point.position;
588            let classification = point.classification;
589            let user_data = point.user_data;
590            assert_eq!(position, position_expected[index]);
591            assert_eq!(classification, 0);
592            assert_eq!(user_data, 0);
593        }
594        Ok(())
595    }
596
597    #[test]
598    #[should_panic(expected = "FormatError can't interpret format literal")]
599    fn test_error_format_unrecognized_literal() {
600        let path = get_test_file_path("10_points_ascii.txt");
601        let reader = BufReader::new(File::open(path).unwrap());
602        RawAsciiReader::from_read(reader, "xyzQ", ", ").unwrap();
603    }
604
605    #[test]
606    #[should_panic(expected = "Input format string expected more items in the line")]
607    fn test_error_format_to_many_literal() {
608        let path = get_test_file_path("10_points_ascii.txt");
609        let reader = BufReader::new(File::open(path).unwrap());
610        let ascii_reader = RawAsciiReader::from_read(reader, "ssssssssx", ", ");
611        ascii_reader.unwrap().read::<VectorBuffer>(10).unwrap();
612    }
613
614    #[test]
615    #[should_panic(expected = "ParseError at parsing Intensity for format literal 'i'")]
616    fn test_error_parse_error_integer() {
617        let path = get_test_file_path("10_points_ascii_parsing_errors.txt");
618        let reader = BufReader::new(File::open(path).unwrap());
619        let ascii_reader = RawAsciiReader::from_read(reader, "sssi", ", ");
620        ascii_reader.unwrap().read::<VectorBuffer>(10).unwrap();
621    }
622    #[test]
623    #[should_panic(expected = "ParseError expected bool found")]
624    fn test_error_parse_error_bool() {
625        let path = get_test_file_path("10_points_ascii_parsing_errors.txt");
626        let reader = BufReader::new(File::open(path).unwrap());
627        let ascii_reader = RawAsciiReader::from_read(reader, "sssse", ", ");
628        ascii_reader.unwrap().read::<VectorBuffer>(10).unwrap();
629    }
630
631    #[test]
632    #[should_panic(expected = "ParseError at parsing CoordinateX for format literal 'x'")]
633    fn test_error_parse_error_float() {
634        let path = get_test_file_path("10_points_ascii_parsing_errors.txt");
635        let reader = BufReader::new(File::open(path).unwrap());
636        let ascii_reader = RawAsciiReader::from_read(reader, "x", ", ");
637        ascii_reader.unwrap().read::<VectorBuffer>(10).unwrap();
638    }
639}