pmtiles 0.2.2

Implementation of the PMTiles v3 spec with multiple sync and async backends.
Documentation
use std::io;
use std::path::Path;

use async_trait::async_trait;
use bytes::{Buf, Bytes};
use fmmap::tokio::{AsyncMmapFile, AsyncMmapFileExt, AsyncOptions};

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

pub struct MmapBackend {
    file: AsyncMmapFile,
}

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

impl From<fmmap::error::Error> for Error {
    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> {
        if self.file.len() >= offset + length {
            Ok(self.file.reader(offset)?.copy_to_bytes(length))
        } else {
            Err(Error::Reading(io::Error::from(
                io::ErrorKind::UnexpectedEof,
            )))
        }
    }

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

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

        Ok(self.file.reader(offset)?.copy_to_bytes(read_length))
    }
}