pcd_rs/
reader.rs

1//! Types for reading PCD data.
2//!
3//! [Reader](crate::reader::Reader) lets you load points sequentially with
4//! [Iterator](std::iter::Iterator) interface. The points are stored in
5//! types implementing [PcdDeserialize](crate::record::PcdDeserialize) trait.
6//! See [record](crate::record) moduel doc to implement your own point type.
7#![cfg_attr(
8    feature = "derive",
9    doc = r##"
10```rust
11use pcd_rs::{PcdDeserialize, Reader};
12use std::path::Path;
13
14#[derive(PcdDeserialize)]
15pub struct Point {
16    x: f32,
17    y: f32,
18    z: f32,
19    rgb: f32,
20}
21
22fn main() -> pcd_rs::Result<()> {
23    let reader = Reader::open("test_files/ascii.pcd")?;
24    let points: Result<Vec<Point>> = reader.collect();
25    assert_eq!(points?.len(), 213);
26    Ok(())
27}
28```
29"##
30)]
31
32use crate::{
33    error::Error,
34    metas::{DataKind, FieldDef, PcdMeta},
35    record::{DynRecord, PcdDeserialize},
36    Result,
37};
38use std::{
39    fs::File,
40    io::{prelude::*, BufReader, Cursor},
41    marker::PhantomData,
42    path::Path,
43};
44
45/// The `DynReader` struct loads points with schema determined in runtime.
46pub type DynReader<R> = Reader<DynRecord, R>;
47
48/// The `Reader<T, R>` struct loads points into type `T` from reader `R`.
49pub struct Reader<T, R>
50where
51    R: Read,
52{
53    meta: PcdMeta,
54    record_count: usize,
55    finished: bool,
56    reader: R,
57    _phantom: PhantomData<T>,
58}
59
60impl<'a, Record> Reader<Record, BufReader<Cursor<&'a [u8]>>>
61where
62    Record: PcdDeserialize,
63{
64    pub fn from_bytes(buf: &'a [u8]) -> Result<Self> {
65        let reader = BufReader::new(Cursor::new(buf));
66        Self::from_reader(reader)
67    }
68}
69
70impl<Record, R> Reader<Record, R>
71where
72    Record: PcdDeserialize,
73    R: BufRead,
74{
75    pub fn from_reader(mut reader: R) -> Result<Self> {
76        let mut line_count = 0;
77        let meta = crate::utils::load_meta(&mut reader, &mut line_count)?;
78
79        // Checks whether the record schema matches the file meta
80        if !Record::is_dynamic() {
81            let record_spec = Record::read_spec();
82
83            macro_rules! bail {
84                () => {
85                    return Err(Error::new_reader_schema_mismatch_error(
86                        record_spec.clone(),
87                        meta.field_defs.fields.clone(),
88                    ));
89                };
90            }
91
92            if record_spec.len() != meta.field_defs.len() {
93                bail!();
94            }
95
96            for (record_field, meta_field) in record_spec.iter().zip(meta.field_defs.iter()) {
97                let (ref name_opt, record_kind, record_count_opt) = *record_field;
98                let FieldDef {
99                    name: ref meta_name,
100                    kind: meta_kind,
101                    count: meta_count,
102                } = *meta_field;
103
104                if record_kind != meta_kind {
105                    bail!();
106                }
107
108                if let Some(name) = &name_opt {
109                    if name != meta_name {
110                        bail!();
111                    }
112                }
113
114                if let Some(record_count) = record_count_opt {
115                    if record_count != meta_count as usize {
116                        bail!();
117                    }
118                }
119            }
120        }
121
122        let pcd_reader = Reader {
123            meta,
124            reader,
125            record_count: 0,
126            finished: false,
127            _phantom: PhantomData,
128        };
129
130        Ok(pcd_reader)
131    }
132}
133
134impl<Record> Reader<Record, BufReader<File>>
135where
136    Record: PcdDeserialize,
137{
138    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
139        let file = BufReader::new(File::open(path.as_ref())?);
140        Self::from_reader(file)
141    }
142}
143
144impl<R, Record> Reader<Record, R>
145where
146    R: BufRead,
147{
148    /// Get meta data.
149    pub fn meta(&self) -> &PcdMeta {
150        &self.meta
151    }
152}
153
154impl<R, Record> Iterator for Reader<Record, R>
155where
156    R: BufRead,
157    Record: PcdDeserialize,
158{
159    type Item = Result<Record>;
160
161    fn next(&mut self) -> Option<Self::Item> {
162        if self.finished {
163            return None;
164        }
165
166        let record_result = match self.meta.data {
167            DataKind::Ascii => Record::read_line(&mut self.reader, &self.meta.field_defs),
168            DataKind::Binary => Record::read_chunk(&mut self.reader, &self.meta.field_defs),
169        };
170
171        match record_result {
172            Ok(_) => {
173                self.record_count += 1;
174                if self.record_count == self.meta.num_points as usize {
175                    self.finished = true;
176                }
177            }
178            Err(_) => {
179                self.finished = true;
180            }
181        }
182
183        Some(record_result)
184    }
185
186    fn size_hint(&self) -> (usize, Option<usize>) {
187        let size = self.meta.num_points as usize;
188        (size, Some(size))
189    }
190}