hive-asar 0.4.0

Asynchronous parser and writer for Electron's asar archive format.
Documentation
use crate::header::{Directory, Entry, FileMetadata};
use std::future::Future;
use std::io::SeekFrom;
use std::path::Path;
use std::pin::Pin;
use tokio::fs::{create_dir, File as TokioFile};
use tokio::io::{self, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt};

macro_rules! impl_extract_entry {
  (
    $extract_entry:ident,
    $extract_dir:ident
    $(, $send:ident)?
  ) => {
    pub fn $extract_entry<'a, R: AsyncRead + AsyncSeek $(+ $send)? + Unpin>(
      reader: &'a mut R,
      offset: u64,
      name: &'a str,
      entry: &'a Entry,
      path: &'a Path,
    ) -> Pin<Box<dyn Future<Output = io::Result<()>> $(+ $send)? + 'a>> {
      Box::pin(async move {
        match entry {
          Entry::File(file) => extract_file(reader, offset, name, file, path).await?,
          Entry::Directory(dir) => $extract_dir(reader, offset, name, dir, path).await?,
        }
        Ok(())
      })
    }
  }
}

impl_extract_entry!(extract_entry, extract_dir, Send);
impl_extract_entry!(extract_entry_local, extract_dir_local);

async fn extract_file<R: AsyncRead + AsyncSeek + Unpin>(
  reader: &mut R,
  offset: u64,
  name: &str,
  file: &FileMetadata,
  path: &Path,
) -> io::Result<()> {
  reader.seek(SeekFrom::Start(offset + file.offset()?)).await?;
  let mut dest = TokioFile::create(path.join(name)).await?;
  io::copy(&mut reader.take(file.size), &mut dest).await?;
  Ok(())
}

macro_rules! impl_extract_dir {
  (
    $extract_dir:ident,
    $extract_entry:ident
    $(, $send:ident)?
  ) => {
    async fn $extract_dir<R: AsyncRead + AsyncSeek $(+ $send)? + Unpin>(
      reader: &mut R,
      offset: u64,
      name: &str,
      dir: &Directory,
      path: &Path,
    ) -> io::Result<()> {
      let new_dir_path = path.join(name);
      create_dir(&new_dir_path).await?;
      for (name, entry) in dir.files.iter() {
        $extract_entry(reader, offset, name, entry, &new_dir_path).await?;
      }
      Ok(())
    }
  }
}

impl_extract_dir!(extract_dir, extract_entry, Send);
impl_extract_dir!(extract_dir_local, extract_entry_local);