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
use de_types::player::Player;
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::{
    hash::MapHasher,
    size::{MapBounds, MapBoundsValidationError},
};

pub const MAX_MAP_NAME_LEN: usize = 16;

/// General information about a map. It does not hold full content of the map
/// (i.e. location of objects on the map).
#[derive(Serialize, Deserialize, Clone)]
pub struct MapMetadata {
    name: String,
    bounds: MapBounds,
    max_player: Player,
}

impl MapMetadata {
    /// Creates a new map description.
    ///
    /// # Arguments
    ///
    /// * `name` - name of the map.
    ///
    /// * `bounds` - bounds of the map.
    ///
    /// * `max_player` - maximum number of players which can play on the map.
    ///   For example, if the value is [de_types::player::Player::Player3], then
    ///   Player1 to `PlayerN` can play.
    ///
    /// # Panics
    ///
    /// Panics if any of the map parameters is invalid.
    pub fn new(name: String, bounds: MapBounds, max_player: Player) -> Self {
        let map = Self {
            name,
            bounds,
            max_player,
        };
        map.validate().unwrap();
        map
    }

    pub(crate) fn update_hash(&self, hasher: &mut MapHasher) {
        hasher.update_str(&self.name);
        hasher.update_vec2(self.bounds.min());
        hasher.update_vec2(self.bounds.max());
        hasher.update_u8(self.max_player.to_num());
    }

    pub fn name(&self) -> &str {
        self.name.as_str()
    }

    pub fn bounds(&self) -> MapBounds {
        self.bounds
    }

    pub fn max_player(&self) -> Player {
        self.max_player
    }

    pub(crate) fn validate(&self) -> Result<(), MapMetadataValidationError> {
        if self.name.is_empty() {
            return Err(MapMetadataValidationError::MapName(
                "map name is empty".into(),
            ));
        }
        if self.name.len() > MAX_MAP_NAME_LEN {
            return Err(MapMetadataValidationError::MapName(format!(
                "map name too long: {} > {}",
                self.name.len(),
                MAX_MAP_NAME_LEN
            )));
        }

        if let Err(error) = self.bounds.validate() {
            return Err(MapMetadataValidationError::MapBounds { source: error });
        }

        if self.max_player < Player::Player2 {
            return Err(MapMetadataValidationError::MaxPlayers(self.max_player));
        }

        Ok(())
    }
}

#[derive(Error, Debug)]
pub enum MapMetadataValidationError {
    #[error("invalid map name: {0}")]
    MapName(String),
    #[error("invalid map bounds")]
    MapBounds { source: MapBoundsValidationError },
    #[error("map has to have at least 2 players, got {0}")]
    MaxPlayers(Player),
}