Documentation
use std::{
  io::{self, BufReader, Cursor, Read},
  pin::Pin,
};

use tar::{Entries, Entry};
use tracing::error;
use zstd::stream::Decoder;

#[derive(Debug)]
pub struct File {
  pub path: String,
  pub bin: Vec<u8>,
}

pub type Reader<'a> = BufReader<Cursor<&'a [u8]>>;

pub fn to_file(entry: io::Result<Entry<Decoder<'_, Reader<'_>>>>) -> Option<File> {
  match entry {
    Ok(mut entry) => {
      let path = match entry.path() {
        Ok(p) => p.to_string_lossy().into_owned(),
        Err(e) => {
          error!("{}", e);
          return None;
        }
      };
      let mut bin = Vec::new();
      match entry.read_to_end(&mut bin) {
        Ok(_) => Some(File { path, bin }),
        Err(e) => {
          error!("{}: {}", path, e);
          None
        }
      }
    }
    Err(e) => {
      error!("{}", e);
      None
    }
  }
}

pub type Archive<'a> = tar::Archive<Decoder<'a, BufReader<Cursor<&'a [u8]>>>>;
pub type ZstDecode<'a> = zstd::Decoder<'a, BufReader<std::io::Cursor<&'a [u8]>>>;

pub struct Tzst<'a> {
  _archive: Pin<Box<Archive<'a>>>,
  entries: Entries<'a, ZstDecode<'a>>,
}

impl<'a> Tzst<'a> {
  pub fn load(data: &'a [u8]) -> io::Result<Self> {
    let cursor = Cursor::new(data);
    let decoder = Decoder::new(cursor)?;
    let archive = tar::Archive::new(decoder);
    let mut _archive = Pin::new(Box::new(archive));
    let archive_ref: &'a mut Archive<'a> =
      unsafe { &mut *(_archive.as_mut().get_unchecked_mut() as *mut Archive<'a>) };

    let entries = archive_ref.entries()?;

    Ok(Tzst { _archive, entries })
  }
}

impl<'a> Iterator for Tzst<'a> {
  type Item = File;

  fn next(&mut self) -> Option<Self::Item> {
    for next in self.entries.by_ref() {
      if let Some(file) = to_file(next) {
        return Some(file);
      }
    }
    None
  }
}

pub fn r(data: &[u8]) -> io::Result<Tzst<'_>> {
  Tzst::load(data)
}