use std::{fs::File, io};
use bao_tree::io::sync::{ReadAt, Size};
use bytes::Bytes;
#[derive(Debug)]
pub enum MemOrFile<M, F> {
    Mem(M),
    File(F),
}
impl<M, F> MemOrFile<M, (F, u64)>
where
    M: AsRef<[u8]>,
{
    pub fn size(&self) -> u64 {
        match self {
            MemOrFile::Mem(mem) => mem.as_ref().len() as u64,
            MemOrFile::File((_, size)) => *size,
        }
    }
}
impl ReadAt for MemOrFile<Bytes, File> {
    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            MemOrFile::Mem(mem) => mem.as_ref().read_at(offset, buf),
            MemOrFile::File(file) => file.read_at(offset, buf),
        }
    }
}
impl Size for MemOrFile<Bytes, File> {
    fn size(&self) -> io::Result<Option<u64>> {
        match self {
            MemOrFile::Mem(mem) => Ok(Some(mem.len() as u64)),
            MemOrFile::File(file) => file.size(),
        }
    }
}
impl<M: Default, F> Default for MemOrFile<M, F> {
    fn default() -> Self {
        MemOrFile::Mem(Default::default())
    }
}
impl<M, F> MemOrFile<M, F> {
    pub fn as_ref(&self) -> MemOrFile<&M, &F> {
        match self {
            MemOrFile::Mem(mem) => MemOrFile::Mem(mem),
            MemOrFile::File(file) => MemOrFile::File(file),
        }
    }
    pub fn is_mem(&self) -> bool {
        matches!(self, MemOrFile::Mem(_))
    }
    pub fn mem(&self) -> Option<&M> {
        match self {
            MemOrFile::Mem(mem) => Some(mem),
            MemOrFile::File(_) => None,
        }
    }
    pub fn map_file<F2>(self, f: impl FnOnce(F) -> F2) -> MemOrFile<M, F2> {
        match self {
            MemOrFile::Mem(mem) => MemOrFile::Mem(mem),
            MemOrFile::File(file) => MemOrFile::File(f(file)),
        }
    }
    pub fn try_map_file<F2, E>(
        self,
        f: impl FnOnce(F) -> Result<F2, E>,
    ) -> Result<MemOrFile<M, F2>, E> {
        match self {
            MemOrFile::Mem(mem) => Ok(MemOrFile::Mem(mem)),
            MemOrFile::File(file) => f(file).map(MemOrFile::File),
        }
    }
    pub fn map_mem<M2>(self, f: impl FnOnce(M) -> M2) -> MemOrFile<M2, F> {
        match self {
            MemOrFile::Mem(mem) => MemOrFile::Mem(f(mem)),
            MemOrFile::File(file) => MemOrFile::File(file),
        }
    }
}