use std::path::Path;
use anyhow::Result;
#[cfg(feature = "cache")]
use anyhow::{anyhow, Context};
use data_request_spec::DataRequestSpec;
use log::{debug, error};
use metadata::Metadata;
use polars::frame::DataFrame;
use search::{Params, SearchParams, SearchResults};
use crate::config::Config;
pub use column_names as COL;
pub mod column_names;
pub mod config;
pub mod data_request_spec;
pub mod error;
#[cfg(feature = "formatters")]
pub mod formatters;
pub mod geo;
pub mod metadata;
pub mod parquet;
pub mod search;
#[derive(Debug, PartialEq)]
pub struct Popgetter {
pub metadata: Metadata,
pub config: Config,
}
impl Popgetter {
pub async fn new() -> Result<Self> {
Self::new_with_config(Config::default()).await
}
pub async fn new_with_config(config: Config) -> Result<Self> {
debug!("config: {config:?}");
let metadata = metadata::load_all(&config).await?;
Ok(Self { metadata, config })
}
#[cfg(feature = "cache")]
pub async fn new_with_config_and_cache(config: Config) -> Result<Self> {
let path = dirs::cache_dir()
.ok_or(anyhow!("Failed to get cache directory"))?
.join("popgetter");
Popgetter::new_with_config_and_cache_path(config, path).await
}
#[cfg(feature = "cache")]
async fn new_with_config_and_cache_path<P: AsRef<Path>>(
config: Config,
path: P,
) -> Result<Self> {
if path.as_ref().exists() {
match Popgetter::new_from_cache_path(config.clone(), &path) {
Ok(popgetter) => return Ok(popgetter),
Err(err) => {
error!("Failed to read metadata from cache with error: {err}");
}
}
}
std::fs::create_dir_all(&path)?;
let popgetter = Popgetter::new_with_config(config).await?;
if let Err(err) = popgetter.metadata.write_cache(&path) {
std::fs::remove_dir_all(&path).with_context(|| {
"Failed to remove cache dir following error writing cache: {err}"
})?;
Err(err)?
}
Ok(popgetter)
}
#[cfg(feature = "cache")]
fn new_from_cache_path<P: AsRef<Path>>(config: Config, path: P) -> Result<Self> {
let metadata = Metadata::from_cache(path)?;
Ok(Self { metadata, config })
}
pub fn search(&self, search_params: &SearchParams) -> SearchResults {
search_params
.clone()
.search(&self.metadata.combined_metric_source_geometry())
}
pub async fn download_data_request_spec(
&self,
data_request_spec: &DataRequestSpec,
) -> Result<DataFrame> {
let params: Params = data_request_spec.clone().try_into()?;
let search_results = self.search(¶ms.search);
search_results
.download(&self.config, ¶ms.download)
.await
}
pub async fn download_params(&self, params: &Params) -> Result<DataFrame> {
self.search(¶ms.search)
.download(&self.config, ¶ms.download)
.await
}
}
#[cfg(test)]
#[cfg(feature = "cache")]
mod tests {
use tempfile::TempDir;
use super::*;
#[tokio::test]
async fn test_popgetter_cache() -> anyhow::Result<()> {
let tempdir = TempDir::new()?;
let config = Config::default();
let popgetter = Popgetter::new_with_config(config.clone()).await?;
popgetter.metadata.write_cache(&tempdir)?;
let popgetter_from_cache =
Popgetter::new_with_config_and_cache_path(config, tempdir).await?;
assert_eq!(popgetter, popgetter_from_cache);
Ok(())
}
}