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
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::{AnimationFrames, from_base36, ToBase36, optional_data_line};
use crate::image::Image;
use crate::mock;

#[derive(Debug, Eq, PartialEq)]
pub struct Tile {
    pub id: u64,
    pub name: Option<String>,
    pub wall: Option<bool>, // this is "optional" in that a tile can have `WAL true|false` or neither
    pub animation_frames: Vec<Image>,
    pub colour_id: Option<u64>,
}

impl Tile {
    fn name_line(&self) -> String {
        optional_data_line("NAME", self.name.as_ref())
    }

    fn wall_line(&self) -> String {
        if self.wall.is_some() {
            format!("\nWAL {}", self.wall.unwrap())
        } else {
            "".to_string()
        }
    }

    fn colour_line(&self) -> String {
        if self.colour_id.is_some() {
            format!("\nCOL {}", self.colour_id.unwrap())
        } else {
            "".to_string()
        }
    }
}

impl From<String> for Tile {
    #[inline]
    fn from(string: String) -> Tile {
        let mut lines: Vec<&str> = string.lines().collect();

        let id = from_base36(&lines[0].replace("TIL ", ""));

        let mut wall = None;
        let mut name = None;
        let mut colour_id = None;

        loop {
            let last_line = lines.pop().unwrap();

            if last_line.starts_with("WAL") {
                wall = Some(last_line.ends_with("true"));
            } else if last_line.starts_with("NAME") {
                name = Some(last_line.replace("NAME ", "").to_string());
            } else if last_line.starts_with("COL") {
                colour_id = Some(last_line.replace("COL ", "").parse().unwrap());
            } else {
                lines.push(last_line);
                break;
            }
        }

        let animation_frames = lines[1..].join("");
        let animation_frames: Vec<&str> = animation_frames.split(">").collect();
        let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
            Image::from(frame.to_string())
        }).collect();

        Tile { id, name, wall, animation_frames, colour_id }
    }
}

impl ToString for Tile {
    #[inline]
    fn to_string(&self) -> String {
        format!(
            "TIL {}\n{}{}{}{}",
            self.id.to_base36(),
            self.animation_frames.to_string(),
            self.name_line(),
            self.wall_line(),
            self.colour_line(),
        )
    }
}

#[test]
fn test_tile_from_string() {
    let output = Tile::from(include_str!("test-resources/tile").to_string());

    let expected = Tile {
        id: 35,
        name: Some("concrete 1".to_string()),
        wall: Some(true),
        animation_frames: vec![
            Image {
                pixels: vec![1; 64]
            }
        ],
        colour_id: None
    };

    assert_eq!(output, expected);
}

#[test]
fn test_tile_to_string() {
    let output = Tile {
        id: 262,
        name: Some("chequers".to_string()),
        wall: None,
        animation_frames: vec![
            mock::image::chequers_1(),
            mock::image::chequers_2(),
        ],
        colour_id: None
    }.to_string();

    let expected = include_str!("test-resources/tile-chequers").to_string();

    assert_eq!(output, expected);
}