Skip to content

Commit

Permalink
Rename Error and HttpError, and move them to root (#25)
Browse files Browse the repository at this point in the history
Even clippy now suggests not to use `Error` name because it is becoming
increasingly conflicting with other libs.  Renamed things to `PmtError`
and `PmtHttpError`.
  • Loading branch information
nyurik authored Nov 10, 2023
1 parent 2de5837 commit ef1aee2
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 49 deletions.
33 changes: 18 additions & 15 deletions src/async_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use reqwest::{Client, IntoUrl};
use tokio::io::AsyncReadExt;

use crate::directory::{Directory, Entry};
use crate::error::Error;
use crate::error::PmtError;
use crate::header::{HEADER_SIZE, MAX_INITIAL_BYTES};
#[cfg(feature = "http-async")]
use crate::http::HttpBackend;
Expand All @@ -29,11 +29,11 @@ impl<B: AsyncBackend + Sync + Send> AsyncPmTilesReader<B> {
/// 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, Error> {
pub async fn try_from_source(backend: B) -> Result<Self, PmtError> {
// 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 {
return Err(Error::InvalidHeader);
return Err(PmtError::InvalidHeader);
}

let header = Header::try_from_bytes(initial_bytes.split_to(HEADER_SIZE))?;
Expand Down Expand Up @@ -73,7 +73,7 @@ impl<B: AsyncBackend + Sync + Send> AsyncPmTilesReader<B> {
///
/// 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, Error> {
pub async fn get_metadata(&self) -> Result<String, PmtError> {
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 @@ -85,13 +85,16 @@ impl<B: AsyncBackend + Sync + Send> AsyncPmTilesReader<B> {
}

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

let meta = self.get_metadata().await?;
let meta: Value = serde_json::from_str(&meta).map_err(|_| Error::InvalidMetadata)?;
let meta: Value = serde_json::from_str(&meta).map_err(|_| PmtError::InvalidMetadata)?;
let Value::Object(meta) = meta else {
return Err(Error::InvalidMetadata);
return Err(PmtError::InvalidMetadata);
};

let mut tj = self.header.get_tilejson(sources);
Expand All @@ -117,7 +120,7 @@ impl<B: AsyncBackend + Sync + Send> AsyncPmTilesReader<B> {
if let Ok(v) = serde_json::from_value::<Vec<tilejson::VectorLayer>>(value) {
tj.vector_layers = Some(v);
} else {
return Err(Error::InvalidMetadata);
return Err(PmtError::InvalidMetadata);
}
} else {
tj.other.insert(key, value);
Expand Down Expand Up @@ -159,20 +162,20 @@ impl<B: AsyncBackend + Sync + Send> AsyncPmTilesReader<B> {
entry.cloned()
}

async fn read_directory(&self, offset: usize, length: usize) -> Result<Directory, Error> {
async fn read_directory(&self, offset: usize, length: usize) -> Result<Directory, PmtError> {
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, Error> {
) -> Result<Directory, PmtError> {
let decompressed_bytes = Self::decompress(compression, bytes).await?;
Directory::try_from(decompressed_bytes)
}

async fn decompress(compression: Compression, bytes: Bytes) -> Result<Bytes, Error> {
async fn decompress(compression: Compression, bytes: Bytes) -> Result<Bytes, PmtError> {
let mut decompressed_bytes = Vec::with_capacity(bytes.len() * 2);
match compression {
Compression::Gzip => {
Expand All @@ -192,7 +195,7 @@ impl AsyncPmTilesReader<HttpBackend> {
/// 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, Error> {
pub async fn new_with_url<U: IntoUrl>(client: Client, url: U) -> Result<Self, PmtError> {
let backend = HttpBackend::try_from(client, url)?;

Self::try_from_source(backend).await
Expand All @@ -204,7 +207,7 @@ impl AsyncPmTilesReader<MmapBackend> {
/// 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, Error> {
pub async fn new_with_path<P: AsRef<Path>>(path: P) -> Result<Self, PmtError> {
let backend = MmapBackend::try_from(path).await?;

Self::try_from_source(backend).await
Expand All @@ -214,10 +217,10 @@ impl AsyncPmTilesReader<MmapBackend> {
#[async_trait]
pub trait AsyncBackend {
/// Reads exactly `length` bytes starting at `offset`
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, Error>;
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, PmtError>;

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

#[cfg(test)]
Expand Down
8 changes: 4 additions & 4 deletions src/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::{Debug, Formatter};
use bytes::{Buf, Bytes};
use varint_rs::VarintReader;

use crate::error::Error;
use crate::error::PmtError;

pub(crate) struct Directory {
entries: Vec<Entry>,
Expand Down Expand Up @@ -38,9 +38,9 @@ impl Directory {
}

impl TryFrom<Bytes> for Directory {
type Error = Error;
type Error = PmtError;

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

Expand Down Expand Up @@ -68,7 +68,7 @@ impl TryFrom<Bytes> for Directory {
for entry in entries.iter_mut() {
let offset = buffer.read_u64_varint()?;
entry.offset = if offset == 0 {
let e = last_entry.ok_or(Error::InvalidEntry)?;
let e = last_entry.ok_or(PmtError::InvalidEntry)?;
e.offset + e.length as u64
} else {
offset - 1
Expand Down
10 changes: 5 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::string::FromUtf8Error;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
pub enum PmtError {
#[error("Invalid magic number")]
InvalidMagicNumber,
#[error("Invalid PMTiles version")]
Expand All @@ -27,12 +27,12 @@ pub enum Error {
UnableToOpenMmapFile,
#[cfg(feature = "http-async")]
#[error("{0}")]
Http(#[from] HttpError),
Http(#[from] PmtHttpError),
}

#[cfg(feature = "http-async")]
#[derive(Debug, Error)]
pub enum HttpError {
pub enum PmtHttpError {
#[error("Unexpected number of bytes returned [expected: {0}, received: {1}].")]
UnexpectedNumberOfBytesReturned(usize, usize),
#[error("Range requests unsupported")]
Expand All @@ -47,8 +47,8 @@ pub enum HttpError {

// This is required because thiserror #[from] does not support two-level conversion.
#[cfg(feature = "http-async")]
impl From<reqwest::Error> for Error {
impl From<reqwest::Error> for PmtError {
fn from(e: reqwest::Error) -> Self {
Self::Http(HttpError::Http(e))
Self::Http(PmtHttpError::Http(e))
}
}
18 changes: 9 additions & 9 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::Error;
use crate::error::PmtError;

#[cfg(any(feature = "http-async", feature = "mmap-async-tokio"))]
pub(crate) const MAX_INITIAL_BYTES: usize = 16_384;
Expand Down Expand Up @@ -59,7 +59,7 @@ impl Compression {
}

impl TryInto<Compression> for u8 {
type Error = Error;
type Error = PmtError;

fn try_into(self) -> Result<Compression, Self::Error> {
match self {
Expand All @@ -68,7 +68,7 @@ impl TryInto<Compression> for u8 {
2 => Ok(Compression::Gzip),
3 => Ok(Compression::Brotli),
4 => Ok(Compression::Zstd),
_ => Err(Error::InvalidCompression),
_ => Err(PmtError::InvalidCompression),
}
}
}
Expand Down Expand Up @@ -125,7 +125,7 @@ impl TileType {
}

impl TryInto<TileType> for u8 {
type Error = Error;
type Error = PmtError;

fn try_into(self) -> Result<TileType, Self::Error> {
match self {
Expand All @@ -134,7 +134,7 @@ impl TryInto<TileType> for u8 {
2 => Ok(TileType::Png),
3 => Ok(TileType::Jpeg),
4 => Ok(TileType::Webp),
_ => Err(Error::InvalidTileType),
_ => Err(PmtError::InvalidTileType),
}
}
}
Expand All @@ -147,15 +147,15 @@ impl Header {
buf.get_i32_le() as f32 / 10_000_000.
}

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

// Assert magic
if magic_bytes != V3_MAGIC {
return Err(if magic_bytes.starts_with(V2_MAGIC.as_bytes()) {
Error::UnsupportedPmTilesVersion
PmtError::UnsupportedPmTilesVersion
} else {
Error::InvalidMagicNumber
PmtError::InvalidMagicNumber
});
}

Expand Down Expand Up @@ -189,7 +189,7 @@ impl Header {
center_latitude: Self::read_coordinate_part(&mut bytes),
})
})
.map_err(|_| Error::InvalidHeader)?
.map_err(|_| PmtError::InvalidHeader)?
}
}

Expand Down
16 changes: 8 additions & 8 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::{Error, HttpError};
use crate::error::{PmtError, PmtHttpError};

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

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

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

if data.len() == length {
Ok(data)
} else {
Err(HttpError::UnexpectedNumberOfBytesReturned(length, data.len()).into())
Err(PmtHttpError::UnexpectedNumberOfBytesReturned(length, data.len()).into())
}
}

async fn read(&self, offset: usize, length: usize) -> Result<Bytes, Error> {
async fn read(&self, offset: usize, length: usize) -> Result<Bytes, PmtError> {
let end = offset + length - 1;
let range = format!("bytes={offset}-{end}");
let range = HeaderValue::try_from(range).map_err(HttpError::from)?;
let range = HeaderValue::try_from(range).map_err(PmtHttpError::from)?;

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

let response = self.client.execute(req).await?.error_for_status()?;
if response.status() != StatusCode::PARTIAL_CONTENT {
return Err(HttpError::RangeRequestsUnsupported.into());
return Err(PmtHttpError::RangeRequestsUnsupported.into());
}

let response_bytes = response.bytes().await?;
if response_bytes.len() > length {
Err(HttpError::ResponseBodyTooLong(response_bytes.len(), length).into())
Err(PmtHttpError::ResponseBodyTooLong(response_bytes.len(), length).into())
} else {
Ok(response_bytes)
}
Expand Down
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
pub use crate::header::{Compression, Header, TileType};

mod directory;
pub mod error;

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

mod header;

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

use crate::async_reader::AsyncBackend;
use crate::error::Error;
use crate::error::PmtError;

pub struct MmapBackend {
file: AsyncMmapFile,
}

impl MmapBackend {
pub async fn try_from<P: AsRef<Path>>(p: P) -> Result<Self, Error> {
pub async fn try_from<P: AsRef<Path>>(p: P) -> Result<Self, PmtError> {
Ok(Self {
file: AsyncMmapFile::open_with_options(p, AsyncOptions::new().read(true))
.await
.map_err(|_| Error::UnableToOpenMmapFile)?,
.map_err(|_| PmtError::UnableToOpenMmapFile)?,
})
}
}

impl From<fmmap::error::Error> for Error {
impl From<fmmap::error::Error> for PmtError {
fn from(_: fmmap::error::Error) -> Self {
Self::Reading(io::Error::from(io::ErrorKind::UnexpectedEof))
}
}

#[async_trait]
impl AsyncBackend for MmapBackend {
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, Error> {
async fn read_exact(&self, offset: usize, length: usize) -> Result<Bytes, PmtError> {
if self.file.len() >= offset + length {
Ok(self.file.reader(offset)?.copy_to_bytes(length))
} else {
Err(Error::Reading(io::Error::from(
Err(PmtError::Reading(io::Error::from(
io::ErrorKind::UnexpectedEof,
)))
}
}

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

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

0 comments on commit ef1aee2

Please sign in to comment.