fastanvil 0.32.0

Minecraft Anvil library
Documentation
use crate::{JavaChunk, LoaderError};
use crate::{LoaderResult, Region};
use crate::{RCoord, RegionLoader};
use std::fs::File;
use std::io::ErrorKind;
use std::marker::PhantomData;
use std::{
    fs,
    path::{Path, PathBuf},
};

pub struct RegionFileLoader {
    region_dir: PathBuf,
    _d: PhantomData<JavaChunk>,
}

impl RegionFileLoader {
    pub fn new(region_dir: PathBuf) -> Self {
        Self {
            region_dir,
            _d: PhantomData,
        }
    }

    pub fn has_region(&self, x: RCoord, z: RCoord) -> bool {
        let path = self.region_dir.join(format!("r.{}.{}.mca", x.0, z.0));
        path.exists()
    }
}

impl RegionLoader<File> for RegionFileLoader {
    fn region(&self, x: RCoord, z: RCoord) -> LoaderResult<Option<Region<File>>> {
        let path = self.region_dir.join(format!("r.{}.{}.mca", x.0, z.0));
        let file = match std::fs::File::open(path) {
            Ok(file) => file,
            Err(e) => {
                if e.kind() == ErrorKind::NotFound {
                    return Ok(None);
                } else {
                    return Err(LoaderError(e.to_string()));
                }
            }
        };
        let region = Region::from_stream(file).map_err(|e| LoaderError(e.to_string()))?;

        Ok(Some(region))
    }

    fn list(&self) -> LoaderResult<Vec<(RCoord, RCoord)>> {
        let paths = std::fs::read_dir(&self.region_dir).map_err(|e| LoaderError(e.to_string()))?;

        let paths = paths
            .into_iter()
            .filter_map(|path| path.ok())
            .map(|path| path.path())
            .filter(|path| path.is_file())
            .filter(|path| {
                let ext = path.extension();
                ext.is_some() && ext.unwrap() == "mca"
            })
            .filter(|path| fs::metadata(path).unwrap().len() > 0)
            .filter_map(|p| coords_from_region(&p))
            .collect();

        Ok(paths)
    }
}

fn coords_from_region(region: &Path) -> Option<(RCoord, RCoord)> {
    let filename = region.file_name()?.to_str()?;
    let mut parts = filename.split('.').skip(1);
    let x = parts.next()?.parse::<isize>().ok()?;
    let z = parts.next()?.parse::<isize>().ok()?;
    Some((RCoord(x), RCoord(z)))
}