Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.7.0: minor cleanups #31

Merged
merged 3 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pmtiles"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
authors = ["Luke Seelenbinder <[email protected]>"]
license = "MIT OR Apache-2.0"
Expand All @@ -12,31 +12,38 @@ categories = ["science::geo"]

[features]
default = []
http-async = ["dep:tokio", "dep:reqwest"]
s3-async-native = ["dep:tokio", "dep:rust-s3", "rust-s3/tokio-native-tls"]
s3-async-rustls = ["dep:tokio", "dep:rust-s3", "rust-s3/tokio-rustls-tls"]
mmap-async-tokio = ["dep:tokio", "dep:fmmap", "fmmap?/tokio-async"]
http-async = ["__async", "dep:reqwest"]
mmap-async-tokio = ["__async", "dep:fmmap", "fmmap?/tokio-async"]
s3-async-native = ["__async-s3"]
s3-async-rustls = ["__async-s3"]
tilejson = ["dep:tilejson", "dep:serde", "dep:serde_json"]

# TODO: support other async libraries
# Forward some of the common features to reqwest dependency
reqwest-native-tls = ["reqwest?/native-tls"]
reqwest-rustls-tls = ["reqwest?/rustls-tls"]
reqwest-rustls-tls-webpki-roots = ["reqwest?/rustls-tls-webpki-roots"]
reqwest-rustls-tls-native-roots = ["reqwest?/rustls-tls-native-roots"]

# Internal features, do not use
__async = ["dep:tokio", "async-compression/tokio"]
__async-s3 = ["__async", "dep:rust-s3", "rust-s3?/tokio-native-tls"]

[dependencies]
# TODO: determine how we want to handle compression in async & sync environments
# TODO: tokio is always requested here, but the tokio dependency is optional below - maybe make it required?
async-compression = { version = "0.4", features = ["gzip", "zstd", "brotli", "tokio"] }
async-compression = { version = "0.4", features = ["gzip", "zstd", "brotli"] }
async-recursion = "1"
async-trait = "0.1"
bytes = "1"
fmmap = { version = "0.3", default-features = false, optional = true }
hilbert_2d = "1"
reqwest = { version = "0.11", default-features = false, optional = true }
rust-s3 = { version = "0.33.0", optional = true, default-features = false, features = ["fail-on-err"] }
serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
thiserror = "1"
tilejson = { version = "0.4", optional = true }
tokio = { version = "1", default-features = false, features = ["io-util"], optional = true }
varint-rs = "2"
rust-s3 = { version = "0.33.0", optional = true, default-features = false, features = ["fail-on-err"] }

[dev-dependencies]
flate2 = "1"
Expand Down
101 changes: 3 additions & 98 deletions src/async_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,14 @@
// so any file larger than 4GB, or an untrusted file with bad data may crash.
#![allow(clippy::cast_possible_truncation)]

#[cfg(feature = "mmap-async-tokio")]
use std::path::Path;

use async_recursion::async_recursion;
use async_trait::async_trait;
use bytes::Bytes;
#[cfg(feature = "http-async")]
use reqwest::{Client, IntoUrl};
#[cfg(any(
feature = "http-async",
feature = "mmap-async-tokio",
feature = "s3-async-rustls",
feature = "s3-async-native"
))]
#[cfg(feature = "__async")]
use tokio::io::AsyncReadExt;

#[cfg(feature = "http-async")]
use crate::backend::HttpBackend;
#[cfg(feature = "mmap-async-tokio")]
use crate::backend::MmapBackend;
#[cfg(any(feature = "s3-async-rustls", feature = "s3-async-native"))]
use crate::backend::S3Backend;
use crate::cache::DirCacheResult;
#[cfg(any(
feature = "http-async",
feature = "mmap-async-tokio",
feature = "s3-async-native",
feature = "s3-async-rustls"
))]
#[cfg(feature = "__async")]
use crate::cache::{DirectoryCache, NoCache};
use crate::directory::{DirEntry, Directory};
use crate::error::{PmtError, PmtResult};
Expand Down Expand Up @@ -227,80 +206,6 @@ impl<B: AsyncBackend + Sync + Send, C: DirectoryCache + Sync + Send> AsyncPmTile
}
}

#[cfg(feature = "http-async")]
impl AsyncPmTilesReader<HttpBackend, NoCache> {
/// Creates a new `PMTiles` reader from a URL using the Reqwest backend.
///
/// Fails if [url] does not exist or is an invalid archive. (Note: HTTP requests are made to validate it.)
pub async fn new_with_url<U: IntoUrl>(client: Client, url: U) -> PmtResult<Self> {
Self::new_with_cached_url(NoCache, client, url).await
}
}

#[cfg(feature = "http-async")]
impl<C: DirectoryCache + Sync + Send> AsyncPmTilesReader<HttpBackend, C> {
/// Creates a new `PMTiles` reader with cache from a URL using the Reqwest backend.
///
/// Fails if [url] does not exist or is an invalid archive. (Note: HTTP requests are made to validate it.)
pub async fn new_with_cached_url<U: IntoUrl>(
cache: C,
client: Client,
url: U,
) -> PmtResult<Self> {
let backend = HttpBackend::try_from(client, url)?;

Self::try_from_cached_source(backend, cache).await
}
}

#[cfg(feature = "mmap-async-tokio")]
impl AsyncPmTilesReader<MmapBackend, NoCache> {
/// Creates a new `PMTiles` reader from a file path using the async mmap backend.
///
/// Fails if [p] does not exist or is an invalid archive.
pub async fn new_with_path<P: AsRef<Path>>(path: P) -> PmtResult<Self> {
Self::new_with_cached_path(NoCache, path).await
}
}

#[cfg(feature = "mmap-async-tokio")]
impl<C: DirectoryCache + Sync + Send> AsyncPmTilesReader<MmapBackend, C> {
/// Creates a new cached `PMTiles` reader from a file path using the async mmap backend.
///
/// Fails if [p] does not exist or is an invalid archive.
pub async fn new_with_cached_path<P: AsRef<Path>>(cache: C, path: P) -> PmtResult<Self> {
let backend = MmapBackend::try_from(path).await?;

Self::try_from_cached_source(backend, cache).await
}
}

#[cfg(any(feature = "s3-async-native", feature = "s3-async-rustls"))]
impl AsyncPmTilesReader<S3Backend, NoCache> {
/// Creates a new `PMTiles` reader from a URL using the Reqwest backend.
///
/// Fails if [url] does not exist or is an invalid archive. (Note: HTTP requests are made to validate it.)
pub async fn new_with_bucket_path(bucket: s3::Bucket, path: String) -> PmtResult<Self> {
Self::new_with_cached_bucket_path(NoCache, bucket, path).await
}
}

#[cfg(any(feature = "s3-async-native", feature = "s3-async-rustls"))]
impl<C: DirectoryCache + Sync + Send> AsyncPmTilesReader<S3Backend, C> {
/// Creates a new `PMTiles` reader with cache from a URL using the Reqwest backend.
///
/// Fails if [url] does not exist or is an invalid archive. (Note: HTTP requests are made to validate it.)
pub async fn new_with_cached_bucket_path(
cache: C,
bucket: s3::Bucket,
path: String,
) -> PmtResult<Self> {
let backend = S3Backend::from(bucket, path);

Self::try_from_cached_source(backend, cache).await
}
}

#[async_trait]
pub trait AsyncBackend {
/// Reads exactly `length` bytes starting at `offset`
Expand All @@ -314,8 +219,8 @@ pub trait AsyncBackend {
#[cfg(feature = "mmap-async-tokio")]
mod tests {
use super::AsyncPmTilesReader;
use crate::backend::MmapBackend;
use crate::tests::{RASTER_FILE, VECTOR_FILE};
use crate::MmapBackend;

#[tokio::test]
async fn open_sanity_check() {
Expand Down
17 changes: 0 additions & 17 deletions src/backend/mod.rs

This file was deleted.

55 changes: 0 additions & 55 deletions src/backend/s3.rs

This file was deleted.

41 changes: 33 additions & 8 deletions src/backend/http.rs → src/backend_http.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
use async_trait::async_trait;
use bytes::Bytes;
use reqwest::{
header::{HeaderValue, RANGE},
Client, IntoUrl, Method, Request, StatusCode, Url,
};
use reqwest::header::{HeaderValue, RANGE};
use reqwest::{Client, IntoUrl, Method, Request, StatusCode, Url};

use crate::{async_reader::AsyncBackend, error::PmtResult, PmtError};
use crate::async_reader::{AsyncBackend, AsyncPmTilesReader};
use crate::cache::{DirectoryCache, NoCache};
use crate::error::PmtResult;
use crate::PmtError;

impl AsyncPmTilesReader<HttpBackend, NoCache> {
/// Creates a new `PMTiles` reader from a URL using the Reqwest backend.
///
/// Fails if [url] does not exist or is an invalid archive. (Note: HTTP requests are made to validate it.)
pub async fn new_with_url<U: IntoUrl>(client: Client, url: U) -> PmtResult<Self> {
Self::new_with_cached_url(NoCache, client, url).await
}
}

impl<C: DirectoryCache + Sync + Send> AsyncPmTilesReader<HttpBackend, C> {
/// Creates a new `PMTiles` reader with cache from a URL using the Reqwest backend.
///
/// Fails if [url] does not exist or is an invalid archive. (Note: HTTP requests are made to validate it.)
pub async fn new_with_cached_url<U: IntoUrl>(
cache: C,
client: Client,
url: U,
) -> PmtResult<Self> {
let backend = HttpBackend::try_from(client, url)?;

Self::try_from_cached_source(backend, cache).await
}
}

pub struct HttpBackend {
client: Client,
pmtiles_url: Url,
url: Url,
}

impl HttpBackend {
pub fn try_from<U: IntoUrl>(client: Client, url: U) -> PmtResult<Self> {
Ok(HttpBackend {
client,
pmtiles_url: url.into_url()?,
url: url.into_url()?,
})
}
}
Expand All @@ -41,7 +66,7 @@ impl AsyncBackend for HttpBackend {
let range = format!("bytes={offset}-{end}");
let range = HeaderValue::try_from(range)?;

let mut req = Request::new(Method::GET, self.pmtiles_url.clone());
let mut req = Request::new(Method::GET, self.url.clone());
req.headers_mut().insert(RANGE, range);

let response = self.client.execute(req).await?.error_for_status()?;
Expand Down
23 changes: 22 additions & 1 deletion src/backend/mmap.rs → src/backend_mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,30 @@ use async_trait::async_trait;
use bytes::{Buf, Bytes};
use fmmap::tokio::{AsyncMmapFile, AsyncMmapFileExt as _, AsyncOptions};

use crate::async_reader::AsyncBackend;
use crate::async_reader::{AsyncBackend, AsyncPmTilesReader};
use crate::cache::{DirectoryCache, NoCache};
use crate::error::{PmtError, PmtResult};

impl AsyncPmTilesReader<MmapBackend, NoCache> {
/// Creates a new `PMTiles` reader from a file path using the async mmap backend.
///
/// Fails if [p] does not exist or is an invalid archive.
pub async fn new_with_path<P: AsRef<Path>>(path: P) -> PmtResult<Self> {
Self::new_with_cached_path(NoCache, path).await
}
}

impl<C: DirectoryCache + Sync + Send> AsyncPmTilesReader<MmapBackend, C> {
/// Creates a new cached `PMTiles` reader from a file path using the async mmap backend.
///
/// Fails if [p] does not exist or is an invalid archive.
pub async fn new_with_cached_path<P: AsRef<Path>>(cache: C, path: P) -> PmtResult<Self> {
let backend = MmapBackend::try_from(path).await?;

Self::try_from_cached_source(backend, cache).await
}
}

pub struct MmapBackend {
file: AsyncMmapFile,
}
Expand Down
Loading
Loading