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}