ogc_cql2/ds/csv.rs
1// SPDX-License-Identifier: Apache-2.0
2
3#![warn(missing_docs)]
4
5//! Geospatial data stored in CSV files.
6//!
7
8use crate::ds::DataSource;
9use std::path::PathBuf;
10
11/// [`DataSource`] of _Features_ and [Resources][crate::Resource] mapped from CSV rows/records.
12#[derive(Debug)]
13pub struct CSVDataSource {
14 path: PathBuf,
15}
16
17impl DataSource for CSVDataSource {
18 fn srid(&self) -> Option<u32> {
19 None
20 }
21}
22
23impl CSVDataSource {
24 /// Constructor given the file system location of an accessible CSV file.
25 pub fn from(s: &str) -> Self {
26 Self { path: s.into() }
27 }
28
29 /// Return this CSV data source path.
30 pub fn path(&self) -> &PathBuf {
31 &self.path
32 }
33}
34
35/// Macro to generate a concrete [CSVDataSource].
36///
37/// Caller must provide the following parameters:
38/// * `$vis`: Visibility specifier of the generated artifacts; e.g. `pub`.
39/// * `$name`: Prefix of the concrete data source structure name to materialize.
40/// The final name will have a 'CSV' suffix appended; eg. `Foo` -> `FooCSV`.
41/// * `$path`: Path to a readable CSV file.
42/// * `$feature`: `serde` deserializable structure that maps rows to _Features_.
43#[macro_export]
44macro_rules! gen_csv_ds {
45 ($vis:vis, $name:expr, $path:expr, $feature:expr) => {
46 ::paste::paste! {
47 /// Concrete data source.
48 #[derive(Debug)]
49 $vis struct [<$name CSV>](CSVDataSource);
50
51 impl [<$name CSV>] {
52 /// Construct a new CSV data source.
53 $vis fn new() -> Self {
54 Self(CSVDataSource::from($path))
55 }
56
57 /// Return a file reader that deserializes rows into features.
58 $vis fn reader(&self) -> Result<::csv::Reader<::std::fs::File>, MyError> {
59 let file = ::std::fs::File::open(self.0.path())?;
60 Ok(::csv::Reader::from_reader(file))
61 }
62 }
63
64 impl ::core::fmt::Display for [<$name CSV>] {
65 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
66 write!(f, "{}CSV({})", $name, $path)
67 }
68 }
69
70 impl IterableDS for [<$name CSV>] {
71 type Item = $feature;
72 type Err = MyError;
73
74 fn iter(&self) -> Result<impl Iterator<Item = Result<$feature, Self::Err>>, Self::Err> {
75 let file = ::std::fs::File::open(&self.0.path())?;
76 let rdr = ::csv::Reader::from_reader(file);
77 let it = rdr.into_deserialize().map(|res| res.map_err(MyError::from));
78 Ok(it)
79 }
80 }
81 }
82 }
83}