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 for (index, line) in (&mut self.reader).lines().take(count).enumerate() {
306 let line = line?;
307 Self::parse_point(&mut temp_point, &line, &self.delimiter, &self.parse_layout)
309 .with_context(|| format!("ReadError in line {}.", index))?;
310 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#[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}