Skip to main content

geonative_core/
dataset.rs

1//! Object-safe `Dataset` + `Layer` traits — the polymorphic surface that
2//! every concrete reader (geonative-filegdb, geonative-shapefile, future
3//! GPKG / GeoJSON / FlatGeoBuf) implements.
4//!
5//! ## When to use traits vs concrete types
6//!
7//! - **Concrete types** (`geonative_filegdb::Geodatabase`,
8//!   `geonative_shapefile::Shapefile`, …) are zero-cost — no virtual
9//!   dispatch, no boxing. Use them when your code knows the format up front.
10//! - **Traits** (`Dataset`, `Layer`) enable format-polymorphic code: a
11//!   converter that opens any source and writes to any sink, a CLI that
12//!   probes by extension, etc. The cost is `Box<dyn>` (one indirection per
13//!   call).
14//!
15//! ## Single-layer formats
16//!
17//! Shapefile-style formats have no concept of multiple layers per file. The
18//! [`SingleLayerDataset`] adapter wraps any [`Layer`] so it can stand in for
19//! a `Dataset` (with a single sentinel layer name, default `"default"`).
20
21use std::sync::Arc;
22
23use crate::{Error, Feature, Result, Schema};
24
25/// A container of one or more named layers — the polymorphic equivalent of
26/// `geonative_filegdb::Geodatabase`.
27pub trait Dataset {
28    /// Names of the user-facing layers in deterministic order.
29    fn layer_names(&self) -> Vec<String>;
30
31    /// Open the layer with the given name. Returns
32    /// [`Error::LayerNotFound`] if the name isn't present.
33    fn open_layer<'a>(&'a self, name: &str) -> Result<Box<dyn Layer + 'a>>;
34}
35
36/// A stream of features sharing one schema — the polymorphic equivalent of
37/// `geonative_filegdb::Layer`, `geonative_shapefile::Shapefile`, etc.
38pub trait Layer {
39    /// Human-friendly layer name (single-layer formats: `"default"`).
40    fn name(&self) -> &str;
41
42    /// The schema of this layer's features.
43    fn schema(&self) -> &Schema;
44
45    /// Declared feature count, if the format exposes it cheaply. Returns
46    /// `None` if computing the count would require a full scan.
47    fn feature_count(&self) -> Option<i64>;
48
49    /// Lazy feature iterator. Each call starts a fresh pass over the layer.
50    fn read<'a>(&'a self) -> Box<dyn Iterator<Item = Result<Feature>> + 'a>;
51}
52
53/// Adapter that wraps any [`Layer`] in a [`Dataset`] facade with one
54/// sentinel layer name. Use this when a single-layer format like Shapefile
55/// needs to satisfy a `Dataset` interface.
56#[derive(Debug)]
57pub struct SingleLayerDataset<L: Layer> {
58    /// The wrapped layer. `Arc` so the adapter is cheap to clone and the
59    /// closure inside `open_layer` can hand out a borrowed reference.
60    pub inner: Arc<L>,
61    /// The synthetic layer name surfaced via [`Dataset::layer_names`].
62    /// Default `"default"`.
63    pub name: String,
64}
65
66impl<L: Layer> SingleLayerDataset<L> {
67    pub fn new(layer: L) -> Self {
68        Self {
69            inner: Arc::new(layer),
70            name: "default".to_string(),
71        }
72    }
73
74    pub fn with_name(layer: L, name: impl Into<String>) -> Self {
75        Self {
76            inner: Arc::new(layer),
77            name: name.into(),
78        }
79    }
80}
81
82impl<L: Layer + 'static> Dataset for SingleLayerDataset<L> {
83    fn layer_names(&self) -> Vec<String> {
84        vec![self.name.clone()]
85    }
86
87    fn open_layer<'a>(&'a self, name: &str) -> Result<Box<dyn Layer + 'a>> {
88        if name == self.name {
89            Ok(Box::new(LayerRef(self.inner.as_ref())))
90        } else {
91            Err(Error::LayerNotFound(name.to_string()))
92        }
93    }
94}
95
96/// Thin wrapper so we can hand out a `Box<dyn Layer>` borrowing from an `Arc<L>`.
97#[derive(Debug)]
98struct LayerRef<'a, L: Layer>(&'a L);
99
100impl<'a, L: Layer> Layer for LayerRef<'a, L> {
101    fn name(&self) -> &str {
102        self.0.name()
103    }
104    fn schema(&self) -> &Schema {
105        self.0.schema()
106    }
107    fn feature_count(&self) -> Option<i64> {
108        self.0.feature_count()
109    }
110    fn read<'b>(&'b self) -> Box<dyn Iterator<Item = Result<Feature>> + 'b> {
111        self.0.read()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use crate::{Crs, FieldDef, GeomField, GeometryType, ValueType};
119
120    /// A trivial in-memory Layer for trait testing.
121    struct TestLayer {
122        name: String,
123        schema: Schema,
124        features: Vec<Feature>,
125    }
126
127    impl Layer for TestLayer {
128        fn name(&self) -> &str {
129            &self.name
130        }
131        fn schema(&self) -> &Schema {
132            &self.schema
133        }
134        fn feature_count(&self) -> Option<i64> {
135            Some(self.features.len() as i64)
136        }
137        fn read<'a>(&'a self) -> Box<dyn Iterator<Item = Result<Feature>> + 'a> {
138            Box::new(self.features.iter().cloned().map(Ok))
139        }
140    }
141
142    fn make_layer() -> TestLayer {
143        TestLayer {
144            name: "roads".into(),
145            schema: Schema::new(
146                vec![FieldDef::new("id", ValueType::Int32, false)],
147                Some(GeomField::new("geom", GeometryType::Point)),
148                Crs::Unknown,
149            ),
150            features: vec![
151                Feature::new(Some(1), None, vec![crate::Value::Int32(1)]),
152                Feature::new(Some(2), None, vec![crate::Value::Int32(2)]),
153            ],
154        }
155    }
156
157    #[test]
158    fn single_layer_dataset_exposes_one_layer() {
159        let ds = SingleLayerDataset::with_name(make_layer(), "default");
160        assert_eq!(ds.layer_names(), vec!["default"]);
161        let layer = ds.open_layer("default").unwrap();
162        assert_eq!(layer.feature_count(), Some(2));
163    }
164
165    #[test]
166    fn single_layer_dataset_rejects_wrong_name() {
167        let ds = SingleLayerDataset::new(make_layer());
168        assert!(ds.open_layer("nope").is_err());
169    }
170
171    #[test]
172    fn layer_read_iterator_yields_features() {
173        let layer = make_layer();
174        let v: Vec<_> = layer.read().collect::<Result<Vec<_>>>().unwrap();
175        assert_eq!(v.len(), 2);
176    }
177
178    #[test]
179    fn dataset_is_object_safe() {
180        // Compile-time check: Box<dyn Dataset> is valid.
181        fn _take_dataset(_: Box<dyn Dataset>) {}
182        let ds: Box<dyn Dataset> = Box::new(SingleLayerDataset::new(make_layer()));
183        _take_dataset(ds);
184    }
185}