use super::cell_type::*;
use super::source_2d::*;
use crate::errors::*;
use crate::mesh_2d::Shape2D;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct Source2DFromText {
width: usize,
data: Vec<Vec<char>>,
}
impl Source2DFromText {
pub const DEFAULT_WALL_CHAR: char = '#';
pub const FREE_CHARS: &'static str = " .o";
pub const DEFAULT_FREE_CHAR: char = ' ';
pub const SEP_CHARS: &'static str = "-_=";
pub const DEFAULT_SEP_CHAR: char = '-';
fn width_from_data(data: &[Vec<char>]) -> usize {
data.iter().map(|l| l.len()).max().unwrap_or(0)
}
fn get_char(&self, x: usize, y: usize) -> Result<char> {
if y >= self.data.len() {
return Err(Error::invalid_xy(x, y));
}
if x >= self.width {
return Err(Error::invalid_xy(x, y));
}
let line = &self.data[y];
if x >= line.len() {
return Ok(Self::DEFAULT_FREE_CHAR);
}
Ok(line[x])
}
pub fn is_wall_char(chr: char) -> bool {
Self::FREE_CHARS.find(chr).is_none() && !Self::is_sep_char(chr)
}
pub fn is_sep_char(chr: char) -> bool {
Self::SEP_CHARS.find(chr).is_some()
}
pub fn from_text(input: &str) -> Source2DFromText {
Self::from_lines(input.lines())
}
pub fn from_slice(input: &[&str]) -> Source2DFromText {
Self::from_lines(input.iter().copied())
}
pub fn from_strings(input: &[String]) -> Source2DFromText {
Self::from_lines(input.iter().map(|s| s.as_str()))
}
pub fn from_lines<'a, I>(input: I) -> Source2DFromText
where
I: Iterator<Item = &'a str>,
{
let data: Vec<Vec<char>> = input
.map(|line| String::from(line).chars().collect())
.collect();
let width = Self::width_from_data(&data);
Source2DFromText { width, data }
}
}
impl FromStr for Source2DFromText {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self::from_text(s))
}
}
impl Source2D for Source2DFromText {
fn get(&self, x: usize, y: usize) -> Result<CellType> {
let chr = self.get_char(x, y)?;
Ok(if Self::is_wall_char(chr) {
CellType::WALL
} else {
CellType::FLOOR
})
}
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.data.len()
}
}
impl Shape2D for Source2DFromText {
fn shape(&self) -> (usize, usize) {
(self.width, self.data.len())
}
}
impl std::fmt::Display for Source2DFromText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Source2DFromText ({}x{}):", self.width(), self.height())?;
write!(f, "{}", crate::mesh_source::repr::repr_source_2d(self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_wall_char() {
assert!(Source2DFromText::is_wall_char('#'));
assert!(Source2DFromText::is_wall_char('a'));
assert!(Source2DFromText::is_wall_char('*'));
assert!(Source2DFromText::is_wall_char('/'));
assert!(!Source2DFromText::is_wall_char('-'));
assert!(!Source2DFromText::is_wall_char('_'));
assert!(!Source2DFromText::is_wall_char('='));
assert!(!Source2DFromText::is_wall_char(' '));
assert!(!Source2DFromText::is_wall_char('.'));
assert!(!Source2DFromText::is_wall_char('o'));
}
#[test]
fn test_is_sep_char() {
assert!(!Source2DFromText::is_sep_char('#'));
assert!(!Source2DFromText::is_sep_char('a'));
assert!(!Source2DFromText::is_sep_char('*'));
assert!(!Source2DFromText::is_sep_char('/'));
assert!(Source2DFromText::is_sep_char('-'));
assert!(Source2DFromText::is_sep_char('_'));
assert!(Source2DFromText::is_sep_char('='));
assert!(!Source2DFromText::is_sep_char(' '));
assert!(!Source2DFromText::is_sep_char('.'));
assert!(!Source2DFromText::is_sep_char('o'));
}
#[test]
fn test_from_text_basic() {
let source = Source2DFromText::from_text("aaaaaa\nb b b \n \n123456");
assert_eq!(source.width(), 6);
assert_eq!(source.height(), 4);
assert_eq!(source.get_char(0, 0).unwrap(), 'a');
assert_eq!(source.get(0, 0).unwrap(), CellType::WALL);
assert_eq!(source.get_char(2, 1).unwrap(), 'b');
assert_eq!(source.get(2, 1).unwrap(), CellType::WALL);
assert_eq!(source.get_char(3, 1).unwrap(), ' ');
assert_eq!(source.get(3, 1).unwrap(), CellType::FLOOR);
assert_eq!(source.get_char(4, 2).unwrap(), ' ');
assert_eq!(source.get(4, 2).unwrap(), CellType::FLOOR);
assert_eq!(source.get_char(5, 3).unwrap(), '6');
assert_eq!(source.get(5, 3).unwrap(), CellType::WALL);
assert!(source.get_char(6, 4).is_err());
assert!(source.get(6, 4).is_err());
}
#[test]
fn test_from_text_last_line() {
let source = Source2DFromText::from_text("aaaaaa\nb b b \n \n123456\n");
assert_eq!(source.width(), 6);
assert_eq!(source.height(), 4);
}
#[test]
fn test_from_text_various_len() {
let source = Source2DFromText::from_text("a\nb b b \n\n123456\n");
assert_eq!(source.width(), 6);
assert_eq!(source.height(), 4);
assert_eq!(source.get_char(0, 0).unwrap(), 'a');
assert_eq!(source.get(0, 0).unwrap(), CellType::WALL);
assert_eq!(source.get_char(2, 1).unwrap(), 'b');
assert_eq!(source.get(2, 1).unwrap(), CellType::WALL);
assert_eq!(source.get_char(3, 1).unwrap(), ' ');
assert_eq!(source.get(3, 1).unwrap(), CellType::FLOOR);
assert_eq!(source.get_char(4, 2).unwrap(), ' ');
assert_eq!(source.get(4, 2).unwrap(), CellType::FLOOR);
assert_eq!(source.get_char(5, 3).unwrap(), '6');
assert_eq!(source.get(5, 3).unwrap(), CellType::WALL);
assert!(source.get_char(6, 4).is_err());
assert!(source.get(6, 4).is_err());
}
}