use std::fmt::Debug;
use std::path::PathBuf;
use std::sync::Arc;
use martin_core::tiles::BoxedSource;
use martin_core::tiles::cog::CogSource;
use serde::ser::SerializeMap as _;
use serde::{Deserialize, Serialize, Serializer};
use url::Url;
use crate::config::file::{
CachePolicy, CollectUnrecognizedKeys, ConfigFileError, ConfigFileResult,
ConfigurationLivecycleHooks, ObjectStoreConfig, SourceBuildResult, TileSourceConfiguration,
UnrecognizedValues,
};
#[derive(Clone, Debug, Default, PartialEq, Deserialize, CollectUnrecognizedKeys)]
#[cfg_attr(feature = "unstable-schemas", derive(schemars::JsonSchema))]
pub struct CogConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "unstable-schemas", schemars(example = &false))]
pub recursive: Option<bool>,
#[serde(default, skip_serializing_if = "CachePolicy::is_empty")]
#[cfg_attr(
feature = "unstable-schemas",
schemars(with = "crate::config::file::CachePolicyShape")
)]
pub cache: CachePolicy,
#[serde(flatten)]
pub object_store: ObjectStoreConfig,
#[serde(flatten, skip_serializing)]
#[cfg_attr(feature = "unstable-schemas", schemars(skip))]
pub unrecognized: UnrecognizedValues,
}
impl Serialize for CogConfig {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
if let Some(recursive) = self.recursive {
map.serialize_entry("recursive", &recursive)?;
}
if !self.cache.is_empty() {
map.serialize_entry("cache", &self.cache)?;
}
self.object_store.serialize_entries(&mut map)?;
map.end()
}
}
impl ConfigurationLivecycleHooks for CogConfig {
async fn finalize(&mut self) -> ConfigFileResult<()> {
self.object_store
.options
.entry("allow_http".to_owned())
.or_insert_with(|| "true".to_owned());
self.object_store.prepare(&mut self.unrecognized, "cog");
self.object_store.finalize_runtime("cog").await;
Ok(())
}
}
impl TileSourceConfiguration for CogConfig {
fn parse_urls() -> bool {
true
}
fn cache(&self) -> CachePolicy {
self.cache
}
async fn new_sources(
&self,
id: String,
path: PathBuf,
cache: CachePolicy,
) -> SourceBuildResult<BoxedSource> {
let cog = CogSource::new(id, path, cache.zoom()).await?;
Ok(Box::new(cog))
}
async fn new_sources_url(
&self,
id: String,
url: Url,
cache: CachePolicy,
) -> SourceBuildResult<BoxedSource> {
let (store, path) = self
.object_store
.parse_url_opts(&url)
.map_err(|e| ConfigFileError::ObjectStoreUrlParsing(e, id.clone()))?;
let source = CogSource::new_object_store(
id,
Arc::from(store),
path,
sanitized_url(&url),
cache.zoom(),
)
.await?;
Ok(Box::new(source))
}
}
fn sanitized_url(url: &Url) -> String {
let mut url = url.clone();
let _ = url.set_username("");
let _ = url.set_password(None);
url.set_query(None);
url.set_fragment(None);
url.to_string()
}