omg_core/data/map/attribute.rs
1use crate::data::math::size2d::Size2d;
2use crate::data::name::validate_name;
3use anyhow::{bail, Result};
4use std::ops::{Index, IndexMut};
5
6/// Represents a value with a specific meaning for each cell of a map.
7///
8/// Examples:
9/// * elevation
10/// * rainfall
11/// * temperature
12#[derive(Clone, Debug, PartialEq)]
13pub struct Attribute {
14 name: String,
15 size: Size2d,
16 values: Vec<u8>,
17}
18
19impl Attribute {
20 /// Creates an attribute filled with a default value.
21 /// See the new constructor for more details.
22 pub fn default_value<S: Into<String>>(name: S, size: Size2d, default: u8) -> Result<Attribute> {
23 let values = vec![default; size.get_area()];
24 Attribute::new(name, size, values)
25 }
26
27 /// Creates an attribute from the supplied values, if their number matches the map size.
28 ///
29 /// ```
30 ///# use omg_core::data::map::attribute::Attribute;
31 ///# use omg_core::data::math::size2d::Size2d;
32 /// assert!(Attribute::new("test", Size2d::unchecked(2, 3), vec![0u8, 50]).is_err());
33 /// ```
34 ///
35 /// Also returns an error, if the name is invalid:
36 ///
37 /// ```
38 ///# use omg_core::data::map::attribute::Attribute;
39 ///# use omg_core::data::math::size2d::Size2d;
40 /// let size = Size2d::unchecked(1, 2);
41 /// assert!(Attribute::new("", size, vec![0u8, 50]).is_err());
42 /// assert!(Attribute::new(" ", size, vec![0u8, 50]).is_err());
43 /// ```
44 pub fn new<S: Into<String>>(name: S, size: Size2d, values: Vec<u8>) -> Result<Attribute> {
45 if size.get_area() != values.len() {
46 bail!(
47 "The size of the map ({}) doesn't match the number of values ({})!",
48 size.get_area(),
49 values.len()
50 );
51 }
52
53 let name = validate_name(name)?;
54
55 Ok(Attribute { name, size, values })
56 }
57
58 /// Returns the name of the attribute.
59 ///
60 /// ```
61 ///# use omg_core::data::map::attribute::Attribute;
62 ///# use omg_core::data::math::size2d::Size2d;
63 /// let attribute = Attribute::default_value("elevation", Size2d::unchecked(2, 3), 42).unwrap();
64 ///
65 /// assert_eq!(attribute.name(), "elevation");
66 /// ```
67 pub fn name(&self) -> &str {
68 &self.name
69 }
70
71 /// Returns the size of the map.
72 ///
73 /// ```
74 ///# use omg_core::data::map::attribute::Attribute;
75 ///# use omg_core::data::math::size2d::Size2d;
76 /// let size = Size2d::unchecked(2, 3);
77 /// let attribute = Attribute::default_value("elevation", size, 42).unwrap();
78 ///
79 /// assert_eq!(attribute.size(), &size);
80 /// ```
81 pub fn size(&self) -> &Size2d {
82 &self.size
83 }
84
85 /// Returns a reference to the values.
86 ///
87 /// ```
88 ///# use omg_core::data::map::attribute::Attribute;
89 ///# use omg_core::data::math::size2d::Size2d;
90 /// let attribute = Attribute::new("elevation", Size2d::unchecked(1, 2), vec![10, 15]).unwrap();
91 ///
92 /// assert_eq!(attribute.get_all(), &vec![10u8, 15u8]);
93 /// ```
94 pub fn get_all(&self) -> &Vec<u8> {
95 &self.values
96 }
97
98 /// Replaces all of the attribute's values.
99 ///
100 /// ```
101 ///# use omg_core::data::map::attribute::Attribute;
102 ///# use omg_core::data::math::size2d::Size2d;
103 /// let mut attribute = Attribute::default_value("elevation", Size2d::unchecked(1, 2), 42).unwrap();
104 ///
105 /// attribute.replace_all(vec![3, 4]);
106 ///
107 /// assert_eq!(attribute.get_all(), &vec![3, 4]);
108 /// ```
109 ///
110 /// # Panics
111 ///
112 /// Panics if the number of new values is wrong.
113 ///
114 /// ```should_panic
115 ///# use omg_core::data::map::attribute::Attribute;
116 ///# use omg_core::data::math::size2d::Size2d;
117 /// let mut attribute = Attribute::default_value("elevation", Size2d::unchecked(1, 2), 42).unwrap();
118 ///
119 /// attribute.replace_all(vec![3, 4, 5]);
120 /// ```
121 pub fn replace_all(&mut self, values: Vec<u8>) {
122 assert_eq!(
123 values.len(),
124 self.values.len(),
125 "Wrong number of new values!"
126 );
127 self.values = values;
128 }
129
130 /// Replaces some of the attribute's values.
131 ///
132 /// ```
133 ///# use omg_core::data::map::attribute::Attribute;
134 ///# use omg_core::data::math::size2d::Size2d;
135 /// let mut attribute = Attribute::default_value("elevation", Size2d::unchecked(1, 3), 42).unwrap();
136 ///
137 /// attribute.replace_some(vec![0, 2], 5);
138 ///
139 /// assert_eq!(attribute.get_all(), &vec![5u8, 42, 5]);
140 /// ```
141 ///
142 /// # Panics
143 ///
144 /// Panics if an index is outside th map.
145 ///
146 /// ```should_panic
147 ///# use omg_core::data::map::attribute::Attribute;
148 ///# use omg_core::data::math::size2d::Size2d;
149 /// let mut attribute = Attribute::default_value("elevation", Size2d::unchecked(1, 2), 42).unwrap();
150 ///
151 /// attribute.replace_some(vec![5], 9);
152 /// ```
153 pub fn replace_some(&mut self, indices: Vec<usize>, value: u8) {
154 for index in indices.iter() {
155 self.values[*index] = value;
156 }
157 }
158}
159
160/// Returns the value at the index.
161///
162/// ```
163///# use omg_core::data::map::attribute::Attribute;
164///# use omg_core::data::math::size2d::Size2d;
165/// let attribute = Attribute::new("elevation", Size2d::unchecked(1, 2), vec![6, 7]).unwrap();
166///
167/// assert_eq!(attribute[0], 6);
168/// assert_eq!(attribute[1], 7);
169/// ```
170///
171/// # Panics
172///
173/// Panics if the index is outside the map.
174///
175/// ```should_panic
176///# use omg_core::data::map::attribute::Attribute;
177///# use omg_core::data::math::size2d::Size2d;
178/// let attribute = Attribute::default_value("elevation", Size2d::unchecked(1, 2), 42).unwrap();
179///
180/// attribute[2];
181/// ```
182impl Index<usize> for Attribute {
183 type Output = u8;
184
185 fn index(&self, index: usize) -> &Self::Output {
186 &self.values[index]
187 }
188}
189
190/// Returns the mutable value at the index.
191///
192/// ```
193///# use omg_core::data::map::attribute::Attribute;
194///# use omg_core::data::math::size2d::Size2d;
195/// let mut attribute = Attribute::default_value("elevation", Size2d::unchecked(1, 2), 42).unwrap();
196///
197/// attribute[0] += 4;
198///
199/// assert_eq!(attribute.get_all(), &vec![46, 42]);
200/// ```
201///
202/// # Panics
203///
204/// Panics if the index is outside the .
205///
206/// ```should_panic
207///# use omg_core::data::map::attribute::Attribute;
208///# use omg_core::data::math::size2d::Size2d;
209/// let mut attribute = Attribute::default_value("elevation", Size2d::unchecked(1, 2), 42).unwrap();
210///
211/// attribute[2] = 99;
212/// ```
213impl IndexMut<usize> for Attribute {
214 fn index_mut(&mut self, index: usize) -> &mut u8 {
215 &mut self.values[index]
216 }
217}