use super::cell_type::*;
use super::source_2d_from_text::*;
use super::source_3d::*;
use crate::errors::*;
use crate::mesh_3d::Shape3D;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct Source3DFromText {
width: usize,
height: usize,
data: Vec<Vec<Vec<char>>>,
}
impl Source3DFromText {
fn width_height_from_data(data: &Vec<Vec<Vec<char>>>) -> (usize, usize) {
let mut width = 0;
let mut height = 0;
for plane in data {
if plane.len() > height {
height = plane.len();
}
for line in plane {
if line.len() > width {
width = line.len();
}
}
}
(width, height)
}
fn get_char(&self, x: usize, y: usize, z: usize) -> Result<char> {
if z >= self.data.len() {
return Err(Error::invalid_xyz(x, y, z));
}
if y >= self.height {
return Err(Error::invalid_xyz(x, y, z));
}
if x >= self.width {
return Err(Error::invalid_xyz(x, y, z));
}
let plane = &self.data[z];
if y >= plane.len() {
return Ok(Source2DFromText::DEFAULT_FREE_CHAR);
}
let line = &plane[y];
if x >= line.len() {
return Ok(Source2DFromText::DEFAULT_FREE_CHAR);
}
Ok(line[x])
}
pub fn is_wall_char(chr: char) -> bool {
Source2DFromText::is_wall_char(chr)
}
pub fn is_sep_line(line: &str) -> bool {
let mut has_sep_char = false;
for chr in line.chars() {
if Source2DFromText::is_sep_char(chr) {
has_sep_char = true;
} else {
if chr != ' ' {
return false;
}
}
}
has_sep_char
}
pub fn from_text(input: &str) -> Source3DFromText {
Self::from_lines(input.lines())
}
pub fn from_slice(input: &[&str]) -> Source3DFromText {
Self::from_lines(input.iter().copied())
}
pub fn from_strings(input: &[String]) -> Source3DFromText {
Self::from_lines(input.iter().map(|s| s.as_str()))
}
pub fn from_lines<'a, I>(input: I) -> Source3DFromText
where
I: Iterator<Item = &'a str>,
{
let mut data: Vec<Vec<Vec<char>>> = Vec::new();
let mut plane_data: Vec<Vec<char>> = Vec::new();
for line in input {
if Self::is_sep_line(line) {
data.push(plane_data);
plane_data = Vec::new();
} else {
let line_data = String::from(line).chars().collect();
plane_data.push(line_data);
}
}
if !plane_data.is_empty() {
data.push(plane_data);
}
let (width, height) = Self::width_height_from_data(&data);
Source3DFromText {
width,
height,
data,
}
}
}
impl FromStr for Source3DFromText {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self::from_text(s))
}
}
impl Source3D for Source3DFromText {
fn get(&self, x: usize, y: usize, z: usize) -> Result<CellType> {
let chr = self.get_char(x, y, z)?;
Ok(if Self::is_wall_char(chr) {
CellType::WALL
} else {
CellType::FLOOR
})
}
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.height
}
fn depth(&self) -> usize {
self.data.len()
}
}
impl Shape3D for Source3DFromText {
fn shape(&self) -> (usize, usize, usize) {
(self.width, self.height, self.data.len())
}
}
impl std::fmt::Display for Source3DFromText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Source3DFromText ({}x{}x{}):", self.width(), self.height(), self.depth())?;
write!(f, "{}", crate::mesh_source::repr::repr_source_3d(self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_sep_line() {
assert!(Source3DFromText::is_sep_line("-"));
assert!(Source3DFromText::is_sep_line("__"));
assert!(Source3DFromText::is_sep_line("== = "));
assert!(!Source3DFromText::is_sep_line(""));
assert!(!Source3DFromText::is_sep_line("abc"));
assert!(!Source3DFromText::is_sep_line("_x_"));
assert!(!Source3DFromText::is_sep_line("."));
assert!(!Source3DFromText::is_sep_line(" "));
}
#[test]
fn test_from_text_basic() {
let source =
Source3DFromText::from_text("aaaaaa\nb b b \n \n123456\n------\n\n xyz\n");
assert_eq!(source.width(), 6);
assert_eq!(source.height(), 4);
assert_eq!(source.depth(), 2);
assert_eq!(source.get_char(0, 0, 0).unwrap(), 'a');
assert_eq!(source.get(0, 0, 0).unwrap(), CellType::WALL);
assert_eq!(source.get_char(2, 1, 0).unwrap(), 'b');
assert_eq!(source.get(2, 1, 0).unwrap(), CellType::WALL);
assert_eq!(source.get_char(3, 1, 0).unwrap(), ' ');
assert_eq!(source.get(3, 1, 0).unwrap(), CellType::FLOOR);
assert_eq!(source.get_char(4, 2, 0).unwrap(), ' ');
assert_eq!(source.get(4, 2, 0).unwrap(), CellType::FLOOR);
assert_eq!(source.get_char(5, 3, 0).unwrap(), '6');
assert_eq!(source.get(5, 3, 0).unwrap(), CellType::WALL);
assert_eq!(source.get_char(0, 0, 1).unwrap(), ' ');
assert_eq!(source.get(0, 0, 1).unwrap(), CellType::FLOOR);
assert_eq!(source.get_char(1, 1, 1).unwrap(), 'x');
assert_eq!(source.get(1, 1, 1).unwrap(), CellType::WALL);
assert!(source.get_char(6, 4, 2).is_err());
assert!(source.get(6, 4, 2).is_err());
}
#[test]
fn test_from_text_eof() {
let source = Source3DFromText::from_text("a\nabc\n====================\n");
assert_eq!(source.width(), 3);
assert_eq!(source.height(), 2);
assert_eq!(source.depth(), 1);
}
}