Skip to content

Commit

Permalink
Introduce PmtResult type (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
nyurik committed Nov 27, 2023
1 parent b2bb41a commit 929281a
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 29 deletions.
31 changes: 14 additions & 17 deletions src/async_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::cache::DirCacheResult;
#[cfg(any(feature = "http-async", feature = "mmap-async-tokio"))]
use crate::cache::{DirectoryCache, NoCache};
use crate::directory::{DirEntry, Directory};
use crate::error::PmtError;
use crate::error::{PmtError, PmtResult};
use crate::header::{HEADER_SIZE, MAX_INITIAL_BYTES};
#[cfg(feature = "http-async")]
use crate::http::HttpBackend;
Expand All @@ -37,7 +37,7 @@ impl<B: AsyncBackend + Sync + Send> AsyncPmTilesReader<B, NoCache> {
/// Creates a new reader from a specified source and validates the provided `PMTiles` archive is valid.
///
/// Note: Prefer using `new_with_*` methods.
pub async fn try_from_source(backend: B) -> Result<Self, PmtError> {
pub async fn try_from_source(backend: B) -> PmtResult<Self> {
Self::try_from_cached_source(backend, NoCache).await
}
}
Expand All @@ -46,7 +46,7 @@ impl<B: AsyncBackend + Sync + Send, C: DirectoryCache + Sync + Send> AsyncPmTile
/// Creates a new cached reader from a specified source and validates the provided `PMTiles` archive is valid.
///
/// Note: Prefer using `new_with_*` methods.
pub async fn try_from_cached_source(backend: B, cache: C) -> Result<Self, PmtError> {
pub async fn try_from_cached_source(backend: B, cache: C) -> PmtResult<Self> {
// Read the first 127 and up to 16,384 bytes to ensure we can initialize the header and root directory.
let mut initial_bytes = backend.read(0, MAX_INITIAL_BYTES).await?;
if initial_bytes.len() < HEADER_SIZE {
Expand Down Expand Up @@ -91,7 +91,7 @@ impl<B: AsyncBackend + Sync + Send, C: DirectoryCache + Sync + Send> AsyncPmTile
///
/// Note: by spec, this should be valid JSON. This method currently returns a [String].
/// This may change in the future.
pub async fn get_metadata(&self) -> Result<String, PmtError> {
pub async fn get_metadata(&self) -> PmtResult<String> {
let offset = self.header.metadata_offset as _;
let length = self.header.metadata_length as _;
let metadata = self.backend.read_exact(offset, length).await?;
Expand All @@ -103,10 +103,7 @@ impl<B: AsyncBackend + Sync + Send, C: DirectoryCache + Sync + Send> AsyncPmTile
}

#[cfg(feature = "tilejson")]
pub async fn parse_tilejson(
&self,
sources: Vec<String>,
) -> Result<tilejson::TileJSON, PmtError> {
pub async fn parse_tilejson(&self, sources: Vec<String>) -> PmtResult<tilejson::TileJSON> {
use serde_json::Value;

let meta = self.get_metadata().await?;
Expand Down Expand Up @@ -190,20 +187,20 @@ impl<B: AsyncBackend + Sync + Send, C: DirectoryCache + Sync + Send> AsyncPmTile
entry
}

async fn read_directory(&self, offset: usize, length: usize) -> Result<Directory, PmtError> {
async fn read_directory(&self, offset: usize, length: usize) -> PmtResult<Directory> {
let data = self.backend.read_exact(offset, length).await?;
Self::read_compressed_directory(self.header.internal_compression, data).await
}

async fn read_compressed_directory(
compression: Compression,
bytes: Bytes,
) -> Result<Directory, PmtError> {
) -> PmtResult<Directory> {
let decompressed_bytes = Self::decompress(compression, bytes).await?;
Directory::try_from(decompressed_bytes)
}

async fn decompress(compression: Compression, bytes: Bytes) -> Result<Bytes, PmtError> {
async fn decompress(compression: Compression, bytes: Bytes) -> PmtResult<Bytes> {
let mut decompressed_bytes = Vec::with_capacity(bytes.len() * 2);
match compression {
Compression::Gzip => {
Expand All @@ -223,7 +220,7 @@ 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) -> Result<Self, PmtError> {
pub async fn new_with_url<U: IntoUrl>(client: Client, url: U) -> PmtResult<Self> {
Self::new_with_cached_url(NoCache, client, url).await
}
}
Expand All @@ -237,7 +234,7 @@ impl<C: DirectoryCache + Sync + Send> AsyncPmTilesReader<HttpBackend, C> {
cache: C,
client: Client,
url: U,
) -> Result<Self, PmtError> {
) -> PmtResult<Self> {
let backend = HttpBackend::try_from(client, url)?;

Self::try_from_cached_source(backend, cache).await
Expand All @@ -249,7 +246,7 @@ 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) -> Result<Self, PmtError> {
pub async fn new_with_path<P: AsRef<Path>>(path: P) -> PmtResult<Self> {
Self::new_with_cached_path(NoCache, path).await
}
}
Expand All @@ -259,7 +256,7 @@ 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) -> Result<Self, PmtError> {
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
Expand All @@ -269,10 +266,10 @@ impl<C: DirectoryCache + Sync + Send> AsyncPmTilesReader<MmapBackend, C> {
#[async_trait]
pub trait AsyncBackend {
/// Reads exactly `length` bytes starting at `offset`
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, PmtError>;
async fn read_exact(&self, offset: usize, length: usize) -> PmtResult<Bytes>;

/// Reads up to `length` bytes starting at `offset`.
async fn read(&self, offset: usize, length: usize) -> Result<Bytes, PmtError>;
async fn read(&self, offset: usize, length: usize) -> PmtResult<Bytes>;
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion src/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Directory {
impl TryFrom<Bytes> for Directory {
type Error = PmtError;

fn try_from(buffer: Bytes) -> Result<Self, PmtError> {
fn try_from(buffer: Bytes) -> Result<Self, Self::Error> {
let mut buffer = buffer.reader();
let n_entries = buffer.read_usize_varint()?;

Expand Down
4 changes: 4 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use std::string::FromUtf8Error;

use thiserror::Error;

/// A specialized [`Result`] type for `PMTiles` operations.
pub type PmtResult<T> = Result<T, PmtError>;

/// Errors that can occur while reading `PMTiles` files.
#[derive(Debug, Error)]
pub enum PmtError {
#[error("Invalid magic number")]
Expand Down
4 changes: 2 additions & 2 deletions src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::panic::catch_unwind;

use bytes::{Buf, Bytes};

use crate::error::PmtError;
use crate::error::{PmtError, PmtResult};

#[cfg(any(feature = "http-async", feature = "mmap-async-tokio"))]
pub(crate) const MAX_INITIAL_BYTES: usize = 16_384;
Expand Down Expand Up @@ -154,7 +154,7 @@ impl Header {
buf.get_i32_le() as f32 / 10_000_000.
}

pub fn try_from_bytes(mut bytes: Bytes) -> Result<Self, PmtError> {
pub fn try_from_bytes(mut bytes: Bytes) -> PmtResult<Self> {
let magic_bytes = bytes.split_to(V3_MAGIC.len());

// Assert magic
Expand Down
8 changes: 4 additions & 4 deletions src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ use reqwest::header::{HeaderValue, RANGE};
use reqwest::{Client, IntoUrl, Method, Request, StatusCode, Url};

use crate::async_reader::AsyncBackend;
use crate::error::{PmtError, PmtHttpError};
use crate::error::{PmtHttpError, PmtResult};

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

impl HttpBackend {
pub fn try_from<U: IntoUrl>(client: Client, url: U) -> Result<Self, PmtError> {
pub fn try_from<U: IntoUrl>(client: Client, url: U) -> PmtResult<Self> {
Ok(HttpBackend {
client,
pmtiles_url: url.into_url()?,
Expand All @@ -22,7 +22,7 @@ impl HttpBackend {

#[async_trait]
impl AsyncBackend for HttpBackend {
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, PmtError> {
async fn read_exact(&self, offset: usize, length: usize) -> PmtResult<Bytes> {
let data = self.read(offset, length).await?;

if data.len() == length {
Expand All @@ -32,7 +32,7 @@ impl AsyncBackend for HttpBackend {
}
}

async fn read(&self, offset: usize, length: usize) -> Result<Bytes, PmtError> {
async fn read(&self, offset: usize, length: usize) -> PmtResult<Bytes> {
let end = offset + length - 1;
let range = format!("bytes={offset}-{end}");
let range = HeaderValue::try_from(range).map_err(PmtHttpError::from)?;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ mod directory;
pub use directory::{DirEntry, Directory};

mod error;
pub use error::PmtError;
#[cfg(feature = "http-async")]
pub use error::PmtHttpError;
pub use error::{PmtError, PmtResult};

#[cfg(feature = "http-async")]
pub mod http;
Expand Down
8 changes: 4 additions & 4 deletions src/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use bytes::{Buf, Bytes};
use fmmap::tokio::{AsyncMmapFile, AsyncMmapFileExt as _, AsyncOptions};

use crate::async_reader::AsyncBackend;
use crate::error::PmtError;
use crate::error::{PmtError, PmtResult};

pub struct MmapBackend {
file: AsyncMmapFile,
}

impl MmapBackend {
pub async fn try_from<P: AsRef<Path>>(p: P) -> Result<Self, PmtError> {
pub async fn try_from<P: AsRef<Path>>(p: P) -> PmtResult<Self> {
Ok(Self {
file: AsyncMmapFile::open_with_options(p, AsyncOptions::new().read(true))
.await
Expand All @@ -30,7 +30,7 @@ impl From<fmmap::error::Error> for PmtError {

#[async_trait]
impl AsyncBackend for MmapBackend {
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, PmtError> {
async fn read_exact(&self, offset: usize, length: usize) -> PmtResult<Bytes> {
if self.file.len() >= offset + length {
Ok(self.file.reader(offset)?.copy_to_bytes(length))
} else {
Expand All @@ -40,7 +40,7 @@ impl AsyncBackend for MmapBackend {
}
}

async fn read(&self, offset: usize, length: usize) -> Result<Bytes, PmtError> {
async fn read(&self, offset: usize, length: usize) -> PmtResult<Bytes> {
let reader = self.file.reader(offset)?;

let read_length = length.min(reader.len());
Expand Down

0 comments on commit 929281a

Please sign in to comment.