tf2_enum/
traits.rs

1use crate::{AttributeDef, AttributeValue, ItemAttribute};
2use crate::error::InsertError;
3
4/// Attribute values for an item attribute.
5pub trait Attribute: Sized {
6    /// The defindex.
7    const DEFINDEX: u32;
8    /// The attribute definition.
9    const ATTRIBUTE: AttributeDef;
10    /// **Not part of the schema.**
11    /// 
12    /// This is a marker to specify which attribute field is meaningful to us in obtaining the
13    /// attribute's value.
14    /// 
15    /// # Kill Count Example
16    /// ```json
17    /// {
18    ///     "defindex": 214,
19    ///     "value": 918,
20    ///     "float_value": 1.28639199025018207e-42
21    /// }
22    /// ```
23    /// This is the "kill_eater" attribute. "918" refers to the number of kills. The `float_value`
24    /// field is the same number as a 32-bit float.
25    /// 
26    /// You can perform the conversions yourself with the following code:
27    /// ```
28    /// let value = 918u32;
29    /// let float_value = 1.28639199025018207e-42f32;
30    /// 
31    /// assert_eq!(f32::from_bits(value), float_value);
32    /// assert_eq!(float_value.to_bits(), value);
33    /// ```
34    /// 
35    /// # Sheen Example
36    /// ```json
37    /// {
38    ///     "defindex": 2014,
39    ///     "value": 1086324736,
40    ///     "float_value": 6
41    /// }
42    /// ```
43    /// This is the "killstreak_idleeffect" attribute. "6" refers to the associated sheen (
44    /// [`Sheen::VillainousViolet`][`crate::Sheen::VillainousViolet`]), but is stored in the
45    /// `float_value` field, unlike "kill_eater". The `value` field is the same number as a 32-bit
46    /// float.
47    /// 
48    /// While both values refer to the same value, and internally the attribute's value is its
49    /// `float_value`, there are many cases where the `float_value` doesn't mean anything to us
50    /// unless converted to a 32-bit integer from its bits, and if the `float_value` does mean
51    /// something to us we don't want to convert it to an integer. This can be a little confusing,
52    /// but it's just how the API is.
53    /// 
54    /// By marking each attribute with a `uses_float_value` flag, we can indicate whether the
55    /// `float_value` field is meaningful to use for that attribute.
56    const USES_FLOAT_VALUE: bool;
57    
58    /// Gets the attribute value.
59    fn attribute_value(&self) -> AttributeValue {
60        self.attribute_float_value()
61            .map(|v| v.to_bits().into())
62            .unwrap_or_default()
63    }
64    
65    /// Gets the attribute float value.
66    fn attribute_float_value(&self) -> Option<f32>;
67}
68
69/// Associated attribute values for a set of item attributes.
70pub trait Attributes: Sized {
71    /// The list of associated defindexes.
72    const DEFINDEX: &'static [u32];
73    /// The attribute definition.
74    const ATTRIBUTES: &'static [AttributeDef];
75    /// See [`Attribute::USES_FLOAT_VALUE`]. This applies to all attributes in the set.
76    const USES_FLOAT_VALUE: bool;
77    
78    /// Gets the attribute value.
79    fn attribute_value(&self) -> AttributeValue {
80        self.attribute_float_value()
81            .map(|v| v.to_bits().into())
82            .unwrap_or_default()
83    }
84    
85    /// Gets the attribute float value.
86    fn attribute_float_value(&self) -> Option<f32> {
87        None
88    }
89    
90    /// Gets the attribute definition for a given defindex.
91    fn get_attribute_def_by_defindex(defindex: u32) -> Option<&'static AttributeDef> {
92        Self::ATTRIBUTES.iter().find(|attr| attr.defindex == defindex)
93    }
94}
95
96/// Backwards conversion for attributes associated with an integer value.
97pub trait TryFromIntAttributeValue: Sized + TryFrom<u32> {
98    /// Attempts conversion from an attribute value.
99    #[allow(unused_variables)]
100    fn try_from_attribute_value(v: AttributeValue) -> Option<Self> {
101        None
102    }
103    
104    /// Attempts conversion from an attribute float value.
105    fn try_from_attribute_float_value(v: f32) -> Option<Self> {
106        if v.fract() != 0.0 || v.is_sign_negative() || v > (u32::MAX as f32) {
107            return None;
108        }
109        
110        Self::try_from(v as u32).ok()
111    }
112}
113
114/// Definitions which are associated with colors.
115pub trait Colored: Sized {
116    /// Gets the color.
117    fn color(&self) -> u32;
118    
119    /// Attempts to convert a hexadecimal color.
120    fn from_color(color: u32) -> Option<Self>;
121    
122    /// Converts this into a hexademical color string in the format "#FFFFFF".
123    #[inline]
124    fn color_string(&self) -> String {
125        format!("#{:06X}", self.color())
126    }
127    
128    /// Attempts to convert a hexadecimal color string.
129    fn from_color_str<S: AsRef<str>>(color: S) -> Option<Self> {
130        Self::from_color(extract_color(color.as_ref())?)
131    }
132}
133
134/// Definitions which are associated with an item defindex.
135pub trait HasItemDefindex: Sized {
136    /// Gets the `defindex`.
137    fn defindex(&self) -> u32;
138    
139    /// Converts a `defindex` into its related item, if it exists.
140    fn from_defindex(defindex: u32) -> Option<Self>;
141}
142
143/// A fixed set of attributes.
144pub trait AttributeSet: Sized + Default {
145    /// Max number of items.
146    const MAX_COUNT: usize;
147    /// The item type.
148    type Item: PartialEq + Copy + Attributes;
149    /// An empty set.
150    const NONE: Self;
151    
152    /// Adds an item to the first available slot. Returns `false` if the set is full or already
153    /// contains the value.
154    fn insert(&mut self, item: Self::Item) -> bool;
155    
156    /// Same as `insert`, but returns a [`std::result::Result`] with descriptive error to identify
157    /// why the insert failed.
158    fn try_insert(&mut self, item: Self::Item) -> Result<(), InsertError>;
159    
160    /// Adds an item to the first available slot. Replaces the last item in the set if the set is
161    /// full. Returns `false` if the set already contains the value.
162    fn insert_or_replace_last(&mut self, item: Self::Item) -> bool;
163    
164    /// Removes an item from the set. Returns whether the value was present in the set.
165    fn remove(&mut self, item: &Self::Item) -> bool;
166    
167    /// Removes and returns the item in the set, if any, that is equal to the given one.
168    fn take(&mut self, item: &Self::Item) -> Option<Self::Item>;
169    
170    /// Replaces an item in the set with a new item. `false` if the item was not present.
171    fn replace(&mut self, item: &Self::Item, new_item: Self::Item) -> bool;
172    
173    /// Clears the set.
174    fn clear(&mut self);
175    
176    /// Gets an item from the set by index.
177    fn get(&self, index: usize) -> Option<&Self::Item> {
178        self.as_slice().get(index).and_then(|opt| opt.as_ref())
179    }
180    
181    /// Returns the number of elements in the set.
182    #[inline]
183    fn len(&self) -> usize {
184        // The sets are small so iteration is fine.
185        self.as_slice()
186            .iter()
187            .filter(|x| x.is_some())
188            .count()
189    }
190
191    /// Returns true if the set contains the given item.
192    fn contains(&self, item: &Self::Item) -> bool {
193        self.as_slice().contains(&Some(*item))
194    }
195    
196    /// Returns true if the set is empty.
197    #[inline]
198    fn is_empty(&self) -> bool {
199        self.len() == 0
200    }
201    
202    /// Returns true if the set is full, i.e., it contains the maximum number of elements.
203    #[inline]
204    fn is_full(&self) -> bool {
205        self.len() == Self::MAX_COUNT
206    }
207    
208    /// Gets the capacity of the set.
209    #[inline]
210    fn capacity(&self) -> usize {
211        Self::MAX_COUNT
212    }
213    
214    /// Gets the first item in the set.
215    fn first(&self) -> Option<&Self::Item> {
216        self.iter().next()
217    }
218    
219    /// Gets the last item in the set.
220    fn last(&self) -> Option<&Self::Item> {
221        self.iter().last()
222    }
223    
224    /// Returns the items that are in `self` but not in `other`.
225    fn difference(&self, other: &Self) -> Self {
226        let mut result = Self::default();
227        
228        for s in self.iter() {
229            if !other.contains(s) {
230                result.insert(*s);
231            }
232        }
233        
234        result
235    }
236    
237    /// Returns the items that are both in `self` and `other`.
238    fn intersection(&self, other: &Self) -> Self {
239        let mut result = Self::default();
240        
241        for s in self.iter() {
242            if other.contains(s) {
243                result.insert(*s);
244            }
245        }
246
247        result
248    }
249    
250    /// Returns `true` if `self` has no items in common with `other`. This is equivalent to 
251    /// checking for an empty intersection.
252    fn is_disjoint(&self, other: &Self) -> bool {
253        self.intersection(other).is_empty()
254    }
255    
256    /// Returns true if the set is a subset of another, i.e., other contains at least all the
257    /// values in self.
258    fn is_subset(&self, other: &Self) -> bool {
259        if self.len() > other.len() {
260            return false;
261        }
262        
263        self.iter().all(|spell| other.contains(spell))
264    }
265    
266    /// Returns true if the set is a superset of another, i.e., self contains at least all the
267    /// values in other.
268    fn is_superset(&self, other: &Self) -> bool {
269        other.is_subset(self)
270    }
271    
272    /// Returns an iterator over the set.
273    #[inline]
274    fn iter(&self) -> impl Iterator<Item = &Self::Item> {
275        self.as_slice().iter().filter_map(|opt| opt.as_ref())
276    }
277    
278    /// Returns a mutable iterator over the set.
279    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item> {
280        self.as_mut_slice().iter_mut().filter_map(|opt| opt.as_mut())
281    }
282    
283    /// Retains only the items specified by the predicate.
284    fn retain<F>(&mut self, mut f: F)
285    where
286        F: FnMut(&Self::Item) -> bool,
287    {
288        for slot in self.as_mut_slice() {
289            if let Some(ref item) = slot {
290                if !f(item) {
291                    *slot = None;
292                }
293            }
294        }
295    }
296    
297    /// Extends items from an iterator into the set.
298    fn extend<I: IntoIterator<Item = Self::Item>>(&mut self, iter: I) {
299        for item in iter {
300            self.insert(item);
301        }
302    }
303    
304    /// Converts each element to an [`ItemAttribute`]. 
305    fn iter_attributes(&self) -> impl Iterator<Item = ItemAttribute>;
306    
307    /// Returns the inner storage as a slice.
308    fn as_slice(&self) -> &[Option<Self::Item>];
309    
310    /// Returns the inner storage as a mutable slice.
311    fn as_mut_slice(&mut self) -> &mut [Option<Self::Item>];
312}
313
314// Compilation optimization.
315// See: <https://matklad.github.io/2021/07/09/inline-in-rust.html>
316fn extract_color(s: &str) -> Option<u32> {
317    let len = s.len();
318    let mut color = s;
319    
320    if len == 7 && color.starts_with('#') {
321        color = &color[1..len];
322    } else if len != 6 {
323        return None;
324    }
325    
326    u32::from_str_radix(color, 16).ok()
327}