shapefile_gbk/
reader.rs

1//! Reader module, contains the definitions of the types that a user should use to read a file
2//!
3//! # Reader
4//!
5//! The [Reader] is the struct that actually reads the different files
6//! that make up a _shapefile_.
7//!
8//! ## Examples
9//!
10//! When reading from a file:
11//!
12//! Creates a reader from a path, then iterate over its `Shapes`, reading one shape each iteration
13//! ```
14//! # fn main() -> Result<(), shapefile::Error> {
15//! let mut reader = shapefile::Reader::from_path("tests/data/multipatch.shp")?;
16//! for shape_record in reader.iter_shapes_and_records() {
17//!     let (shape, record) = shape_record?;
18//!     println!("{}", shape);
19//! }
20//! # Ok(())
21//! # }
22//! ```
23//!
24//! # ShapeReader
25//!
26//! If you only care about the geometries stored in the _.shp_ file, whether or not the _.dbf_ file
27//! actually exists, you can use the [ShapeReader].
28//!
29//! # Extra
30//!
31//! If you know beforehand the exact type that the .shp file is made of,
32//! you can use the different `*_as::<S>()` methods.:
33//! - [Reader::read_as] To read all the shapes and records as the specified types using a Reader
34//! - [Reader::iter_shapes_and_records_as] To iterate over both the shapes and records of the Reader
35//!
36//! - [ShapeReader::read_as] To read all the shapes (only) as the specified type using ShapeReader
37//! - [ShapeReader::iter_shapes_as] To iterate over the shapes as shapes
38//!   of the specified type using ShapeReader
39//!
40//!
41//! Otherwise use the functions that return [Shapes](../record/enum.Shape.html) and do a `match`
42//!
43//! - [Reader::read]
44//! - [Reader::iter_shapes_and_records]
45//! - [ShapeReader::read]
46//! - [ShapeReader::iter_shapes]
47//!
48//! ## One liners
49//!
50//! Some 'one liner' functions are provided to read the content
51//! of a shapefile with one line of code
52//!
53//! - [read]
54//! - [read_as]
55//! - [read_shapes]
56//! - [read_shapes_as]
57
58use std::fs::File;
59use std::io::{BufReader, Read, Seek, SeekFrom, Write};
60use std::path::Path;
61
62use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
63use encoding_rs::GBK;
64
65use crate::header;
66use crate::record;
67use crate::record::ReadableShape;
68use crate::{Error, Shape};
69
70const INDEX_RECORD_SIZE: usize = 2 * std::mem::size_of::<i32>();
71
72#[derive(Copy, Clone)]
73pub(crate) struct ShapeIndex {
74    pub offset: i32,
75    pub record_size: i32,
76}
77
78impl ShapeIndex {
79    pub(crate) fn write_to<W: Write>(self, dest: &mut W) -> std::io::Result<()> {
80        dest.write_i32::<BigEndian>(self.offset)?;
81        dest.write_i32::<BigEndian>(self.record_size)?;
82        Ok(())
83    }
84}
85
86/// Read the content of a .shx file
87fn read_index_file<T: Read>(mut source: T) -> Result<Vec<ShapeIndex>, Error> {
88    let header = header::Header::read_from(&mut source)?;
89
90    let num_shapes = ((header.file_length * 2) - header::HEADER_SIZE) / INDEX_RECORD_SIZE as i32;
91    let mut shapes_index = Vec::<ShapeIndex>::with_capacity(num_shapes as usize);
92    for _ in 0..num_shapes {
93        let offset = source.read_i32::<BigEndian>()?;
94        let record_size = source.read_i32::<BigEndian>()?;
95        shapes_index.push(ShapeIndex {
96            offset,
97            record_size,
98        });
99    }
100    Ok(shapes_index)
101}
102
103/// Reads and returns one shape and its header from the source
104fn read_one_shape_as<T: Read, S: ReadableShape>(
105    mut source: &mut T,
106) -> Result<(record::RecordHeader, S), Error> {
107    let hdr = record::RecordHeader::read_from(&mut source)?;
108    let record_size = hdr.record_size * 2;
109    let shape = S::read_from(&mut source, record_size)?;
110    Ok((hdr, shape))
111}
112
113/// Struct that handle iteration over the shapes of a .shp file
114pub struct ShapeIterator<'a, T: Read, S: ReadableShape> {
115    _shape: std::marker::PhantomData<S>,
116    // From where we read the shapes
117    source: &'a mut T,
118    // Current position in bytes in the source.
119    current_pos: usize,
120    // How many bytes the header said there are in
121    // the file.
122    file_length: usize,
123    // Iterator over the shape indices, used to seek
124    // to the start of a shape when reading
125    shapes_indices: Option<std::slice::Iter<'a, ShapeIndex>>,
126}
127
128impl<'a, T: Read + Seek, S: ReadableShape> Iterator for ShapeIterator<'a, T, S> {
129    type Item = Result<S, crate::Error>;
130
131    fn next(&mut self) -> Option<Self::Item> {
132        if self.current_pos >= self.file_length {
133            None
134        } else {
135            if let Some(ref mut shapes_indices) = self.shapes_indices {
136                // Its 'safer' to seek to the shape offset when we have the `shx` file
137                // as some shapes may not be stored sequentially and may contain 'garbage'
138                // bytes between them
139                let start_pos = shapes_indices.next()?.offset * 2;
140                if start_pos != self.current_pos as i32 {
141                    if let Err(err) = self.source.seek(SeekFrom::Start(start_pos as u64)) {
142                        return Some(Err(err.into()));
143                    }
144                    self.current_pos = start_pos as usize;
145                }
146            }
147            let (hdr, shape) = match read_one_shape_as::<T, S>(self.source) {
148                Err(e) => return Some(Err(e)),
149                Ok(hdr_and_shape) => hdr_and_shape,
150            };
151            self.current_pos += record::RecordHeader::SIZE;
152            self.current_pos += hdr.record_size as usize * 2;
153            Some(Ok(shape))
154        }
155    }
156
157    fn size_hint(&self) -> (usize, Option<usize>) {
158        self.shapes_indices
159            .as_ref()
160            .map(|s| s.size_hint())
161            .unwrap_or((0, None))
162    }
163}
164
165pub struct ShapeRecordIterator<
166    'a,
167    T: Read + Seek,
168    D: Read + Seek,
169    S: ReadableShape,
170    R: dbase::ReadableRecord,
171> {
172    shape_iter: ShapeIterator<'a, T, S>,
173    record_iter: dbase::RecordIterator<'a, D, R>,
174}
175
176impl<'a, T: Read + Seek, D: Read + Seek, S: ReadableShape, R: dbase::ReadableRecord> Iterator
177    for ShapeRecordIterator<'a, T, D, S, R>
178{
179    type Item = Result<(S, R), Error>;
180
181    fn next(&mut self) -> Option<Self::Item> {
182        let shape = match self.shape_iter.next()? {
183            Err(e) => return Some(Err(e)),
184            Ok(shp) => shp,
185        };
186
187        let record = match self.record_iter.next()? {
188            Err(e) => return Some(Err(Error::DbaseError(e))),
189            Ok(rcd) => rcd,
190        };
191
192        Some(Ok((shape, record)))
193    }
194}
195
196/// This reader only reads the `.shp` and optionally the (`.shx`) files
197/// of a shapefile.
198pub struct ShapeReader<T> {
199    source: T,
200    header: header::Header,
201    shapes_index: Option<Vec<ShapeIndex>>,
202}
203
204impl<T: Read> ShapeReader<T> {
205    /// Creates a new ShapeReader from a source that implements the `Read` trait
206    ///
207    /// The Shapefile header is read upon creation (but no reading of the Shapes is done)
208    ///
209    /// # Errors
210    ///
211    /// Will forward any `std::io::Error`
212    ///
213    /// Will also return an error if the data is not a shapefile (Wrong file code)
214    ///
215    /// Will also return an error if the shapetype read from the input source is invalid
216    ///
217    /// # Example
218    ///
219    /// ```
220    /// # fn main() -> Result<(), shapefile::Error> {
221    /// use std::fs::File;
222    /// let file = File::open("tests/data/line.shp")?;
223    /// let reader = shapefile::ShapeReader::new(file)?;
224    /// # Ok(())
225    /// # }
226    /// ```
227    pub fn new(mut source: T) -> Result<Self, Error> {
228        let header = header::Header::read_from(&mut source)?;
229
230        Ok(Self {
231            source,
232            header,
233            shapes_index: None,
234        })
235    }
236
237    /// Creates a new ShapeReader using 2 sources, one for the _.shp_
238    /// the other for the _.shx_
239    ///
240    /// The _.shp_ header is read upon creation
241    /// and the whole _.shx_ file is read upon creation.
242    ///
243    /// # Example
244    /// ```no_run
245    /// # fn main() -> Result<(), shapefile::Error> {
246    /// use std::fs::File;
247    /// let shp_file = File::open("cities.shp")?;
248    /// let shx_file = File::open("cities.shx:")?;
249    /// let reader = shapefile::ShapeReader::with_shx(shp_file, shx_file)?;
250    /// # Ok(())
251    /// # }
252    /// ```
253    pub fn with_shx<ShxSource>(mut source: T, shx_source: ShxSource) -> Result<Self, Error>
254    where
255        ShxSource: Read,
256    {
257        let shapes_index = Some(read_index_file(shx_source)?);
258        let header = header::Header::read_from(&mut source)?;
259
260        Ok(Self {
261            source,
262            header,
263            shapes_index,
264        })
265    }
266
267    /// Returns a non-mutable reference to the header read
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// # fn main() -> Result<(), shapefile::Error> {
273    /// let reader = shapefile::ShapeReader::from_path("tests/data/pointz.shp")?;
274    /// let header = reader.header();
275    /// assert_eq!(header.shape_type, shapefile::ShapeType::PointZ);
276    /// # Ok(())
277    /// # }
278    /// ```
279    pub fn header(&self) -> &header::Header {
280        &self.header
281    }
282}
283
284impl<T: Read + Seek> ShapeReader<T> {
285    /// Reads all the shape as shape of a certain type.
286    ///
287    /// To be used if you know in advance which shape type the file contains.
288    ///
289    /// # Errors
290    /// The function has an additional error that is returned if  the shape type you asked to be
291    /// read does not match the actual shape type in the file.
292    ///
293    /// # Examples
294    ///
295    /// ```
296    /// # fn main() -> Result<(), shapefile::Error> {
297    /// use shapefile::ShapeReader;
298    /// let mut reader = ShapeReader::from_path("tests/data/linem.shp")?;
299    /// let polylines_m = reader.read_as::<shapefile::PolylineM>(); // we ask for the correct type
300    /// assert_eq!(polylines_m.is_ok(), true);
301    /// # Ok(())
302    /// # }
303    /// ```
304    ///
305    /// ```
306    /// # fn main() -> Result<(), shapefile::Error> {
307    /// use shapefile::ShapeReader;
308    /// let mut reader = ShapeReader::from_path("tests/data/linem.shp")?;
309    /// let polylines = reader.read_as::<shapefile::Polyline>(); // we ask for the wrong type
310    /// assert_eq!(polylines.is_err(), true);
311    /// # Ok(())
312    /// # }
313    /// ```
314    pub fn read_as<S: ReadableShape>(mut self) -> Result<Vec<S>, Error> {
315        self.iter_shapes_as::<S>().collect()
316    }
317
318    /// Reads all the shapes and returns them
319    ///
320    /// # Examples
321    /// ```
322    /// # fn main() -> Result<(), shapefile::Error> {
323    /// let mut reader = shapefile::ShapeReader::from_path("tests/data/multipoint.shp")?;
324    /// let shapes = reader.read()?;
325    /// for shape in shapes {
326    ///     match shape {
327    ///         shapefile::Shape::Multipoint(pts) => println!(" Yay Multipoints: {}", pts),
328    ///         _ => panic!("ups, not multipoints"),
329    ///     }
330    /// }
331    /// # Ok(())
332    /// # }
333    /// ```
334    ///
335    pub fn read(mut self) -> Result<Vec<Shape>, Error> {
336        self.iter_shapes_as::<Shape>().collect()
337    }
338
339    /// Returns an iterator that tries to read the shapes as the specified type
340    /// Will return an error of the type `S` does not match the actual type in the file
341    ///
342    /// # Examples
343    ///
344    /// ```
345    /// # fn main() -> Result<(), shapefile::Error> {
346    /// let mut reader = shapefile::ShapeReader::from_path("tests/data/multipoint.shp")?;
347    /// for multipoints in reader.iter_shapes_as::<shapefile::Multipoint>() {
348    ///     let points = multipoints?;
349    ///     println!("{}", points);
350    /// }
351    /// # Ok(())
352    /// # }
353    /// ```
354    pub fn iter_shapes_as<S: ReadableShape>(&mut self) -> ShapeIterator<'_, T, S> {
355        ShapeIterator {
356            _shape: std::marker::PhantomData,
357            source: &mut self.source,
358            current_pos: header::HEADER_SIZE as usize,
359            file_length: (self.header.file_length as usize) * 2,
360            shapes_indices: self.shapes_index.as_ref().map(|s| s.iter()),
361        }
362    }
363
364    /// Returns an iterator that to reads the shapes wraps them in the enum [Shape](enum.Shape.html)
365    /// You do not need to call this method and can iterate over the `Reader` directly
366    ///
367    /// # Examples
368    ///
369    /// ```
370    /// # fn main() -> Result<(), shapefile::Error> {
371    /// let mut reader = shapefile::ShapeReader::from_path("tests/data/multipoint.shp")?;
372    /// for shape in reader.iter_shapes() {
373    ///     match shape? {
374    ///         shapefile::Shape::Multipatch(shp) => println!("Multipoint!"),
375    ///         _ => println!("Other type of shape"),
376    ///     }
377    /// }
378    /// # Ok(())
379    /// # }
380    /// ```
381    ///
382    /// ```
383    /// # fn main() -> Result<(), shapefile::Error> {
384    /// let mut reader = shapefile::ShapeReader::from_path("tests/data/multipoint.shp")?;
385    /// for shape in reader.iter_shapes() {
386    ///     match shape? {
387    ///         shapefile::Shape::Multipatch(shp) => println!("Multipoint!"),
388    ///         _ => println!("Other type of shape"),
389    ///     }
390    /// }
391    /// # Ok(())
392    /// # }
393    /// ```
394    pub fn iter_shapes(&mut self) -> ShapeIterator<'_, T, Shape> {
395        self.iter_shapes_as::<Shape>()
396    }
397
398    /// Reads the `n`th shape of the shapefile
399    ///
400    /// # Important
401    ///
402    /// Even though in shapefiles, shapes are indexed starting from '1'.
403    /// this method expects indexes starting from 0.
404    ///
405    /// # Returns
406    ///
407    /// `None` if the index is out of range
408    ///
409    /// # Errors
410    ///
411    /// This method will return an `Error::MissingIndexFile` if you use it
412    /// but no *.shx* was found when opening the shapefile.
413    pub fn read_nth_shape_as<S: ReadableShape>(
414        &mut self,
415        index: usize,
416    ) -> Option<Result<S, Error>> {
417        if let Some(ref shapes_index) = self.shapes_index {
418            if index >= shapes_index.len() {
419                return None;
420            }
421
422            if let Err(e) = self.seek(index) {
423                return Some(Err(e));
424            }
425
426            let (_, shape) = match read_one_shape_as::<T, S>(&mut self.source) {
427                Err(e) => return Some(Err(e)),
428                Ok(hdr_and_shape) => hdr_and_shape,
429            };
430
431            if let Err(e) = self
432                .source
433                .seek(SeekFrom::Start(header::HEADER_SIZE as u64))
434            {
435                return Some(Err(Error::IoError(e)));
436            }
437            Some(Ok(shape))
438        } else {
439            Some(Err(Error::MissingIndexFile))
440        }
441    }
442
443    /// Reads the `n`th shape of the shapefile
444    pub fn read_nth_shape(&mut self, index: usize) -> Option<Result<Shape, Error>> {
445        self.read_nth_shape_as::<Shape>(index)
446    }
447
448    /// Seek to the start of the shape at `index`
449    ///
450    /// # Error
451    ///
452    /// Returns [Error::MissingIndexFile] if the _shx_ file
453    /// was not found by [ShapeReader::from_path] or the reader
454    /// was not constructed with [ShapeReader::with_shx]
455    pub fn seek(&mut self, index: usize) -> Result<(), Error> {
456        if let Some(ref shapes_index) = self.shapes_index {
457            let offset = shapes_index
458                .get(index)
459                .map(|shape_idx| (shape_idx.offset * 2) as u64);
460
461            match offset {
462                Some(n) => self.source.seek(SeekFrom::Start(n)),
463                None => self.source.seek(SeekFrom::End(0)),
464            }?;
465            Ok(())
466        } else {
467            Err(Error::MissingIndexFile)
468        }
469    }
470
471    /// Returns the number of shapes in the shapefile
472    ///
473    /// # Error
474    ///
475    /// Returns [Error::MissingIndexFile] if the _shx_ file
476    /// was not found by [ShapeReader::from_path] or the reader
477    /// was not constructed with [ShapeReader::with_shx]
478    ///
479    /// # Example
480    ///
481    /// ```
482    /// let reader = shapefile::ShapeReader::from_path("tests/data/point.shp").unwrap();
483    /// // point.shp has a .shx file next to it, so we can read the count data
484    /// assert_eq!(1, reader.shape_count().unwrap());
485    ///
486    /// let reader = shapefile::ShapeReader::from_path("tests/data/pointm.shp").unwrap();
487    /// // There is no pointm.shx, so the shape_count() method returns error
488    /// assert!(reader.shape_count().is_err(), "Should return error if no index file");
489    /// ```
490    pub fn shape_count(&self) -> Result<usize, Error> {
491        if let Some(ref shapes_index) = self.shapes_index {
492            Ok(shapes_index.len())
493        } else {
494            Err(Error::MissingIndexFile)
495        }
496    }
497}
498
499impl ShapeReader<BufReader<File>> {
500    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
501        let shape_path = path.as_ref().to_path_buf();
502        let shx_path = shape_path.with_extension("shx");
503
504        let source = BufReader::new(File::open(shape_path)?);
505
506        if shx_path.exists() {
507            let index_source = BufReader::new(File::open(shx_path)?);
508            Self::with_shx(source, index_source)
509        } else {
510            Self::new(source)
511        }
512    }
513}
514
515/// Reader that reads a _shapefile_.
516///
517/// The recommended way to create a _Reader_ is by using its
518/// [Reader::from_path] associated function as it
519/// will take care of opening the _.shx_ and _.dbf_ files corresponding to the _.shp_
520/// and return an error in case some mandatory files are missing.
521///
522///
523/// If you want to read a shapefile that is not stored in a file
524/// (e.g the shp data is in a buffer), you will have to construct
525/// the *Reader* "by hand" with its [Reader::new] associated function.
526pub struct Reader<T: Read + Seek, D: Read + Seek> {
527    shape_reader: ShapeReader<T>,
528    dbase_reader: dbase::Reader<D>,
529}
530
531impl<T: Read + Seek, D: Read + Seek> Reader<T, D> {
532    /// Creates a new Reader from both a ShapeReader (.shp, .shx) and dbase::Reader (.dbf)
533    pub fn new(shape_reader: ShapeReader<T>, dbase_reader: dbase::Reader<D>) -> Self {
534        Self {
535            shape_reader,
536            dbase_reader,
537        }
538    }
539
540    /// Returns the header of the .shp file
541    pub fn header(&self) -> &header::Header {
542        self.shape_reader.header()
543    }
544
545    pub fn iter_shapes_and_records_as<S: ReadableShape, R: dbase::ReadableRecord>(
546        &mut self,
547    ) -> ShapeRecordIterator<'_, T, D, S, R> {
548        ShapeRecordIterator {
549            shape_iter: self.shape_reader.iter_shapes_as::<S>(),
550            record_iter: self.dbase_reader.iter_records_as::<R>(),
551        }
552    }
553
554    /// Returns an iterator that returns both the shape and the record
555    ///
556    /// # Example
557    ///
558    /// ```
559    /// # fn main() -> Result<(), shapefile::Error> {
560    /// let mut reader = shapefile::Reader::from_path("tests/data/multipatch.shp")?;
561    /// for shape_record in reader.iter_shapes_and_records() {
562    ///     let (shape, record) = shape_record?;
563    ///     println!("Geometry: {}, Properties {:?}", shape, record);
564    /// }
565    /// # Ok(())
566    /// # }
567    /// ```
568    pub fn iter_shapes_and_records(
569        &mut self,
570    ) -> ShapeRecordIterator<'_, T, D, Shape, dbase::Record> {
571        self.iter_shapes_and_records_as::<Shape, dbase::Record>()
572    }
573
574    pub fn read_as<S: ReadableShape, R: dbase::ReadableRecord>(
575        &mut self,
576    ) -> Result<Vec<(S, R)>, Error> {
577        self.iter_shapes_and_records_as::<S, R>().collect()
578    }
579
580    /// Read all the shape and record and returns them in a [Vec]
581    ///
582    /// # Example
583    ///
584    /// ```
585    /// # fn main() -> Result<(), shapefile::Error> {
586    /// let mut reader = shapefile::Reader::from_path("tests/data/multipatch.shp")?;
587    /// let data = reader.read()?;
588    /// # Ok(())
589    /// # }
590    /// ```
591    pub fn read(&mut self) -> Result<Vec<(Shape, dbase::Record)>, Error> {
592        self.read_as::<Shape, dbase::Record>()
593    }
594
595    /// Seeks to the start of the shape at `index`
596    pub fn seek(&mut self, index: usize) -> Result<(), Error> {
597        self.shape_reader.seek(index)?;
598        self.dbase_reader.seek(index)?;
599        Ok(())
600    }
601
602    /// Returns the number of shapes in the shapefile
603    ///
604    /// See [ShapeReader::shape_count]
605    pub fn shape_count(&self) -> Result<usize, Error> {
606        self.shape_reader.shape_count()
607    }
608
609    /// Consumes the self and returns the dbase table info
610    /// which can be given to [TableWriterBuild](dbase::TableWriterBuilder) or
611    /// [crate::Writer::from_path_with_info] to create a shapefile where the .dbf file has the
612    /// same structure as the .dbf read by this reader
613    pub fn into_table_info(self) -> dbase::TableInfo {
614        self.dbase_reader.into_table_info()
615    }
616}
617
618impl Reader<BufReader<File>, BufReader<File>> {
619    /// Creates a reader from a path the .shp file
620    ///
621    /// Will attempt to read both the `.shx` and `.dbf` associated with the file,
622    ///
623    /// If the `.shx` is not found, no error will be returned,
624    /// however [seek] will fail is used.
625    ///
626    /// If the `.dbf` is not found [Error::MissingDbf] will be return as the error.
627    ///
628    /// [seek]: ShapeReader::seek
629    ///
630    /// # Examples
631    ///
632    /// ```
633    /// use std::path::Path;
634    /// # fn main() -> Result<(), shapefile::Error> {
635    /// assert_eq!(Path::new("tests/data/linem.dbf").exists(), false);
636    /// assert_eq!(Path::new("tests/data/linem.shx").exists(), false);
637    ///
638    /// // .dbf file not found, the reader can't be created
639    /// let mut reader = shapefile::Reader::from_path("tests/data/linem.shp");
640    /// assert_eq!(reader.is_err(), true);
641    ///
642    /// assert_eq!(Path::new("tests/data/multipatch.dbf").exists(), true);
643    ///
644    /// // The dbf file exists, the reader can be created
645    /// let mut reader = shapefile::Reader::from_path("tests/data/multipatch.shp");
646    /// assert_eq!(reader.is_err(),  false);
647    /// # Ok(())
648    /// # }
649    /// ```
650    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
651        let shape_path = path.as_ref().to_path_buf();
652        let dbf_path = shape_path.with_extension("dbf");
653
654        if dbf_path.exists() {
655            let shape_reader = ShapeReader::from_path(path)?;
656            // let dbf_source = BufReader::new(File::open(dbf_path)?);
657            // let dbf_reader = dbase::Reader::new(dbf_source)?;
658            let dbf_reader = dbase::Reader::from_path_with_encoding(
659                dbf_path,
660                dbase::encoding::EncodingRs::from(GBK),
661            )?;
662
663            Ok(Self {
664                shape_reader,
665                dbase_reader: dbf_reader,
666            })
667        } else {
668            Err(Error::MissingDbf)
669        }
670    }
671}
672
673pub fn read<T: AsRef<Path>>(path: T) -> Result<Vec<(Shape, dbase::Record)>, Error> {
674    read_as::<T, Shape, dbase::Record>(path)
675}
676
677pub fn read_as<T: AsRef<Path>, S: ReadableShape, R: dbase::ReadableRecord>(
678    path: T,
679) -> Result<Vec<(S, R)>, Error> {
680    Reader::from_path(path).and_then(|mut rdr| rdr.read_as::<S, R>())
681}
682
683/// Function to read all the Shapes in a file as a certain type
684///
685/// It does not open the .dbf file.
686///
687/// Fails and return `Err(Error:MismatchShapeType)`
688///
689///  # Examples
690///
691/// ```
692/// let polylines = shapefile::read_shapes_as::<_, shapefile::PolylineZ>("tests/data/polygon.shp");
693/// assert_eq!(polylines.is_err(), true);
694///
695/// let polygons = shapefile::read_shapes_as::<_, shapefile::Polygon>("tests/data/polygon.shp");
696/// assert_eq!(polygons.is_ok(), true);
697/// ```
698///
699/// If the reading is successful, the returned `Vec<S:ReadShape>>`is a vector of actual structs
700/// Useful if you know in at compile time which kind of shape you expect the file to have
701pub fn read_shapes_as<T: AsRef<Path>, S: ReadableShape>(path: T) -> Result<Vec<S>, Error> {
702    ShapeReader::from_path(path).and_then(|rdr| rdr.read_as::<S>())
703}
704
705/// Function to read all the Shapes in a file.
706///
707/// Returns a `Vec<Shape>` which means that you will have to `match`
708/// the individual [Shape](enum.Shape.html) contained inside the `Vec` to get the actual `struct`
709///
710/// Useful if you don't know in advance (at compile time) which kind of
711/// shape the file contains
712///
713/// # Examples
714///
715/// ```
716/// # fn main() -> Result<(), shapefile::Error> {
717/// let shapes = shapefile::read_shapes("tests/data/multipatch.shp")?;
718/// assert_eq!(shapes.len(), 1);
719/// # Ok(())
720/// # }
721/// ```
722pub fn read_shapes<T: AsRef<Path>>(path: T) -> Result<Vec<Shape>, Error> {
723    read_shapes_as::<T, Shape>(path)
724}
725
726#[cfg(test)]
727mod tests {}