lootr/
item.rs

1//! Module containing item types used in Lootr.
2//!
3//! Items are the core data type used to hold your items data in Lootr.
4//! It holds a `name` and some `props`.
5//!
6//! The easiest way to create an Item is to use [`Item::from`](crate::item::Item::from).
7//!
8//! Item [`Props`](crate::item::Props) can be queried directly with `has_prop()`, `get_prop()` and `set_prop()`
9//!
10
11use std::{
12    collections::HashMap,
13    fmt::{self, format, Display},
14};
15
16/// Holds the item properties in an `HashMap<&str, &str>`.
17///
18pub type Props<'a> = HashMap<&'a str, &'a str>;
19
20/// Holds a modifier helper function.
21///
22pub type Modifier = fn(item: Item) -> Item;
23
24/// Holds a Lootr Item.
25///
26/// Items are the core data type used to hold your items data in Lootr.
27/// It holds a `name` and some `props`.
28///
29/// The easiest way to create an Item is to use [`Item::from`](crate::item::Item::from).
30///
31#[derive(Debug, Clone)]
32pub struct Item<'a> {
33    /// Holds the item name.
34    ///
35    pub name: &'a str,
36
37    /// Holds the item properties.
38    ///
39    pub props: Option<Props<'a>>,
40}
41
42impl<'a> Display for Item<'a> {
43    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44        let props = self.props.clone().unwrap_or_default();
45        let props: Vec<String> = props
46            .iter()
47            .map(|(key, value)| format(format_args!("{}={}", key, value)))
48            .collect::<_>();
49        write!(f, "{}{{{}}}", self.name, props.join(","))
50    }
51}
52
53impl<'a> Item<'a> {
54    /// Create an Item with just a name.
55    ///
56    /// # Examples
57    ///
58    /// ```
59    /// use lootr::item::Item;
60    ///
61    /// let hat = Item::a("hat");
62    /// ```
63    pub fn a(name: &'a str) -> Self {
64        Self { name, props: None }
65    }
66
67    /// Create an Item with just a name.
68    ///
69    /// * `name: &str` Item name
70    ///
71    /// # Examples
72    ///
73    /// ```
74    /// use lootr::item::Item;
75    ///
76    /// let hat = Item::an("ascot");
77    /// ```
78    pub fn an(name: &'a str) -> Self {
79        Item::a(name)
80    }
81
82    /// Create an Item with just a name.
83    ///
84    /// * `name: &str` Item name
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use lootr::item::Item;
90    ///
91    /// let hat = Item::named("greg");
92    /// ```
93    pub fn named(name: &'a str) -> Self {
94        Item::a(name)
95    }
96
97    /// Create an Item with a name and some properties.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use lootr::item::{Item, Props};
103    ///
104    /// let hat = Item::from("hat", Props::from([
105    ///     ("color", "black"),
106    ///     ("size", "small"),
107    /// ]));
108    /// ```
109    pub fn from(name: &'a str, props: Props<'a>) -> Self {
110        Item {
111            name,
112            props: Some(props),
113        }
114    }
115
116    /// Create an Item by extending a previous one, with new name and properties.
117    /// The given properties will overload the given item ones.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use lootr::item::{Item, Props};
123    ///
124    /// let hat = Item::from("hat", Props::from([
125    ///     ("color", "black"),
126    ///     ("size", "large"),
127    /// ]));
128    ///
129    /// let cap = hat.extend("cap", Props::from([
130    ///     ("size", "small"),
131    /// ]));
132    ///
133    /// assert_eq!(cap.get_prop("color"), Some("black"));
134    /// assert_eq!(cap.get_prop("size"), Some("small"));
135    /// ```
136    pub fn extend(&self, name: &'a str, ext_props: Props<'a>) -> Self {
137        let mut new_props: HashMap<&str, &str> = HashMap::new();
138        new_props.extend(self.props.clone().unwrap_or_default().iter());
139        new_props.extend(ext_props.iter());
140
141        Item {
142            name,
143            props: Some(new_props),
144        }
145    }
146
147    /// Check the existence of an item property.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use lootr::item::{Item, Props};
153    ///
154    /// let hat = Item::from("hat", Props::from([
155    ///     ("color", "black"),
156    ///     ("size", "small"),
157    /// ]));
158    ///
159    /// assert_eq!(hat.has_prop("size"), true)
160    /// ```
161    pub fn has_prop(&self, key: &str) -> bool {
162        match &self.props {
163            None => false,
164            Some(props) => props.contains_key(key),
165        }
166    }
167
168    /// Return an item property.
169    /// If this prop does not exist, `None` is returned.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use lootr::item::{Item, Props};
175    ///
176    /// let hat = Item::from("hat", Props::from([
177    ///     ("color", "black"),
178    ///     ("size", "small"),
179    /// ]));
180    ///
181    /// assert_eq!(hat.get_prop("size"), Some("small"))
182    /// ```
183    pub fn get_prop(&self, key: &str) -> Option<&str> {
184        match &self.props {
185            None => None,
186            Some(props) => props.get(key).copied(),
187        }
188    }
189
190    /// Set an item property.
191    /// If this prop already exist, the value is replaced.
192    ///
193    /// # Examples
194    ///
195    /// ```
196    /// use lootr::item::{Item, Props};
197    ///
198    /// let mut hat = Item::from("hat", Props::from([
199    ///     ("color", "black"),
200    ///     ("size", "small"),
201    /// ]));
202    ///
203    /// hat.set_prop("fancy", "yes");
204    /// hat.set_prop("size", "large");
205    ///
206    /// assert_eq!(hat.get_prop("fancy"), Some("yes"));
207    /// assert_eq!(hat.get_prop("size"), Some("large"));
208    /// ```
209    pub fn set_prop<'b: 'a>(&mut self, key: &'b str, value: &'b str) -> &mut Self {
210        let mut new_props: HashMap<&str, &str> = HashMap::new();
211        new_props.extend(self.props.clone().unwrap_or_default().iter());
212        new_props.insert(key, value);
213        self.props = Some(new_props);
214
215        self
216    }
217}