Skip to content

Commit

Permalink
Iterator for RegionFolderProvider (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zettymaster authored Mar 21, 2021
1 parent b2c0221 commit 58884b2
Showing 1 changed file with 67 additions and 2 deletions.
69 changes: 67 additions & 2 deletions src/provider.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::position::RegionPosition;
use crate::region::Region;
use std::fs::{File, OpenOptions};
use std::fs::{File, OpenOptions, read_dir};
use std::path::Path;
use std::{fs, io};
use std::str::FromStr;

pub trait RegionProvider<S> {
fn get_region(&self, region_pos: RegionPosition) -> Result<Region<S>, io::Error>;
Expand All @@ -19,6 +20,17 @@ impl<'a> FolderRegionProvider<'a> {

FolderRegionProvider { folder_path }
}

// leave implementing this to the specific provider,
// makes function declaration bearable for now
pub fn iter_positions(&self) -> Result<impl Iterator<Item=RegionPosition>, io::Error> {
let positions: Vec<_> = read_dir(self.folder_path)?
.filter_map(|dir| dir.ok())
.filter_map(|dir| region_pos_from_filename(&dir.path()).ok())
.collect();

Ok(positions.into_iter())
}
}

impl<'a> RegionProvider<File> for FolderRegionProvider<'a> {
Expand All @@ -27,7 +39,7 @@ impl<'a> RegionProvider<File> for FolderRegionProvider<'a> {
fs::create_dir(self.folder_path)?;
}

let region_name = format!("r.{}.{}.mca", position.x, position.z);
let region_name = region_position_filename(position);
let region_path = self.folder_path.join(region_name);

let file = OpenOptions::new()
Expand All @@ -39,3 +51,56 @@ impl<'a> RegionProvider<File> for FolderRegionProvider<'a> {
Region::load(position, file)
}
}

fn region_pos_from_filename(path: &Path) -> Result<RegionPosition, io::Error> {
// we can use lossy because of the bound check later
let filename = path.file_name().unwrap_or_default().to_string_lossy();
let parts: Vec<_> = filename.split('.').collect();

let (x, z) = parse_coords(parts).ok_or_else(|| io::ErrorKind::InvalidInput)?;

Ok(RegionPosition::new(x, z))
}

fn region_position_filename(pos: RegionPosition) -> String {
format!("r.{}.{}.mca", pos.x, pos.z)
}

fn parse_coords(parts: Vec<&str>) -> Option<(i32, i32)> {
let incorrect_format =
parts.len() != 4 ||
parts[0] != "r" ||
parts[3] != "mca";

if incorrect_format {
return None;
}

Some((i32::from_str(parts[1]).ok()?,
i32::from_str(parts[2]).ok()?))
}

#[cfg(test)]
mod tests {
use std::path::PathBuf;
use crate::position::RegionPosition;
use crate::provider::region_pos_from_filename;

#[test]
fn test_position_parse() {
let mut path = PathBuf::new();
path.set_file_name("r.-1.1.mca");

let pos = region_pos_from_filename(&path).unwrap();
assert_eq!(RegionPosition{ x: -1, z: 1}, pos)
}

#[test]
#[should_panic]
fn test_position_parse_invalid_format() {
let mut path = PathBuf::new();
path.set_file_name("this is not a valid region.filename");

region_pos_from_filename(&path).unwrap();
}
}

0 comments on commit 58884b2

Please sign in to comment.