mp4-edit 0.1.1

mp4 read/write library designed with audiobooks in mind
Documentation
use futures_io::{AsyncRead, AsyncSeek};
use futures_util::io::{AsyncReadExt, AsyncSeekExt};
use std::io::SeekFrom;
use std::marker::PhantomData;

use crate::parser::ParseErrorKind;
use crate::ParseError;

mod sealed {
    pub trait Sealed {}
}

pub struct Seekable;
pub struct NonSeekable;

impl sealed::Sealed for Seekable {}
impl sealed::Sealed for NonSeekable {}

pub trait ReadCapability: sealed::Sealed {}

impl ReadCapability for NonSeekable {}

impl ReadCapability for Seekable {}

pub struct Mp4Reader<R, C: ReadCapability> {
    reader: R,
    pub(crate) current_offset: usize,
    peek_buffer: Vec<u8>,
    _capability: PhantomData<C>,
}

impl<R, C: ReadCapability> Mp4Reader<R, C> {
    pub fn into_inner(self) -> (Vec<u8>, R) {
        (self.peek_buffer, self.reader)
    }
}

impl<R: AsyncRead + Unpin + Send> Mp4Reader<R, NonSeekable> {
    pub fn new(reader: R) -> Self {
        Self {
            reader,
            current_offset: 0,
            peek_buffer: Vec::new(),
            _capability: PhantomData,
        }
    }
}

impl<R: AsyncRead + Unpin + Send, C: ReadCapability> Mp4Reader<R, C> {
    pub(crate) async fn peek_exact(&mut self, buf: &mut [u8]) -> Result<(), ParseError> {
        let size = buf.len();
        if self.peek_buffer.len() < size {
            let mut temp_buf = vec![0u8; size - self.peek_buffer.len()];
            self.reader.read_exact(&mut temp_buf).await.map_err(|e| {
                if e.kind() == std::io::ErrorKind::UnexpectedEof {
                    return ParseError {
                        kind: ParseErrorKind::Eof,
                        location: Some((self.current_offset, size)),
                        source: Some(Box::new(e)),
                    };
                }
                ParseError {
                    kind: ParseErrorKind::Io,
                    location: Some((self.current_offset, size)),
                    source: Some(Box::new(e)),
                }
            })?;
            self.peek_buffer.extend_from_slice(&temp_buf[..]);
        }
        buf.copy_from_slice(&self.peek_buffer[..size]);
        Ok(())
    }

    pub(crate) async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ParseError> {
        self.peek_exact(buf).await?;
        self.peek_buffer.drain(..buf.len());
        self.current_offset += buf.len();
        Ok(())
    }

    pub(crate) async fn read_data(&mut self, size: usize) -> Result<Vec<u8>, ParseError> {
        let mut data = vec![0u8; size];
        self.read_exact(&mut data).await?;
        Ok(data)
    }
}

impl<R: AsyncRead + AsyncSeek + Unpin + Send> Mp4Reader<R, Seekable> {
    pub fn new(reader: R) -> Self {
        Self {
            reader,
            current_offset: 0,
            peek_buffer: Vec::new(),
            _capability: PhantomData,
        }
    }

    pub(crate) async fn seek(&mut self, pos: SeekFrom) -> Result<(), ParseError> {
        match self.reader.seek(pos).await {
            Ok(offset) => {
                self.current_offset = offset as usize;
                self.peek_buffer = Vec::new();
                Ok(())
            }
            Err(err) => Err(ParseError {
                kind: ParseErrorKind::Io,
                location: None,
                source: Some(Box::new(err)),
            }),
        }
    }
}