bbox_feature_server/datasource/
mod.rs

1//! Feature source implementations.
2
3use crate::config::{CollectionSourceCfg, ConfiguredCollectionCfg};
4use crate::error::{Error, Result};
5use crate::filter_params::FilterParams;
6use crate::inventory::FeatureCollection;
7use async_trait::async_trait;
8use bbox_core::config::{DatasourceCfg, NamedDatasourceCfg};
9use bbox_core::ogcapi::{CoreExtent, CoreFeature, Queryables};
10use bbox_core::NamedObjectStore;
11use dyn_clone::{clone_trait_object, DynClone};
12use std::env;
13
14pub mod gpkg;
15pub mod postgis;
16
17#[async_trait]
18pub trait CollectionDatasource {
19    async fn setup_collection(
20        &mut self,
21        cfg: &ConfiguredCollectionCfg,
22        extent: Option<CoreExtent>,
23    ) -> Result<FeatureCollection>;
24}
25
26#[async_trait]
27pub trait AutoscanCollectionDatasource {
28    async fn collections(&mut self) -> Result<Vec<FeatureCollection>>;
29}
30
31#[async_trait]
32pub trait CollectionSource: DynClone + Sync + Send {
33    async fn items(&self, filter: &FilterParams) -> Result<ItemsResult>;
34    async fn item(&self, collection_id: &str, feature_id: &str) -> Result<Option<CoreFeature>>;
35    async fn queryables(&self, collection_id: &str) -> Result<Option<Queryables>>;
36}
37
38clone_trait_object!(CollectionSource);
39
40/// Datasource connection pools
41#[derive(Default)]
42pub struct Datasources {
43    pg_datasources: NamedObjectStore<postgis::Datasource>,
44    gpkg_datasources: NamedObjectStore<gpkg::Datasource>,
45}
46
47impl Datasources {
48    pub async fn create(datasources: &Vec<NamedDatasourceCfg>) -> Result<Self> {
49        // TODO: setup referenced datasources only (?)
50        let mut ds_handler = Datasources::default();
51        for named_ds in datasources {
52            // TODO: check duplicate names
53            // TODO: move into core, combined with tile-server Datasource
54            let envar = env::var(format!("BBOX_DATASOURCE_{}", &named_ds.name.to_uppercase())).ok();
55            match &named_ds.datasource {
56                DatasourceCfg::Postgis(cfg) => {
57                    let ds = postgis::Datasource::from_config(cfg, envar)
58                        .await
59                        .map_err(|e| Error::DatasourceSetupError(e.to_string()))?;
60                    ds_handler.pg_datasources.add(&named_ds.name, ds);
61                }
62                DatasourceCfg::Gpkg(cfg) => {
63                    let ds = gpkg::Datasource::from_config(cfg).await?;
64                    ds_handler.gpkg_datasources.add(&named_ds.name, ds);
65                }
66                _ => { /* ignore others */ }
67            }
68        }
69        Ok(ds_handler)
70    }
71    pub async fn setup_collection(
72        &mut self,
73        collection: &ConfiguredCollectionCfg,
74    ) -> Result<FeatureCollection> {
75        match &collection.source {
76            CollectionSourceCfg::Postgis(cfg) => {
77                let source = self
78                    .pg_datasources
79                    .get_or_default_mut(cfg.datasource.as_deref())
80                    .ok_or(Error::DatasourceNotFound(
81                        cfg.datasource
82                            .as_ref()
83                            .unwrap_or(&"(default)".to_string())
84                            .clone(),
85                    ))?;
86                source.setup_collection(collection, None).await
87            }
88            CollectionSourceCfg::Gpkg(ref cfg) => {
89                let source = self
90                    .gpkg_datasources
91                    .get_or_default_mut(cfg.datasource.as_deref())
92                    .ok_or(Error::DatasourceNotFound(
93                        cfg.datasource
94                            .as_ref()
95                            .unwrap_or(&"(default)".to_string())
96                            .clone(),
97                    ))?;
98                source.setup_collection(collection, None).await
99            }
100        }
101    }
102}
103
104#[derive(Debug)]
105pub struct ItemsResult {
106    pub features: Vec<CoreFeature>,
107    pub number_matched: u64,
108    pub number_returned: u64,
109}