1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
use serde::de::DeserializeOwned;

use crate::{Chunk, LoaderError};
use crate::{LoaderResult, RegionBuffer};
use crate::{RCoord, Region, RegionLoader};
use std::marker::PhantomData;
use std::{
    fs,
    path::{Path, PathBuf},
};

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

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

impl<C: Chunk + DeserializeOwned> RegionLoader<C> for RegionFileLoader<C> {
    fn region(&self, x: RCoord, z: RCoord) -> Option<Box<dyn Region<C>>> {
        let path = self.region_dir.join(format!("r.{}.{}.mca", x.0, z.0));
        let file = std::fs::File::open(path).ok()?;
        let region = RegionBuffer::new(file);

        Some(Box::new(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)))
}