inv_sys/
lib.rs

1#![doc(html_root_url = "https://docs.rs/inv-sys/1.4.1")]
2
3use std::fmt::Debug;
4
5pub trait Stacksize {
6	fn get_max_stacksize(&self) -> usize;
7}
8
9#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
10pub struct Inv<T> {
11	slots: Vec<Slot<T>>
12}
13
14impl<T> IntoIterator for Inv<T> {
15	type Item = Slot<T>;
16	type IntoIter = std::vec::IntoIter<Self::Item>;
17
18	fn into_iter(self) -> Self::IntoIter {
19		self.slots.into_iter()
20	}
21}
22
23#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
24pub struct Slot<T> {
25	inner: Option<ItemStack<T>>
26}
27
28impl<T> Slot<T>
29where T: Stacksize + Eq + Clone + Ord {
30	/// Creates an empty slot
31	pub fn new_empty() -> Self {
32		Self {
33			inner: None
34		}
35	}
36
37	/// Creates a new Slot with a given Itemstack
38	pub fn new(items: ItemStack<T>) -> Self {
39		Self {
40			inner: Some(items)
41		}
42	}
43
44	/// Returns true if the Slot is empty
45	pub fn is_empty(&self) -> bool {
46		self.inner.is_none()
47	}
48
49	/// Tops this slot up with a given ItemStack
50	pub fn stack(&mut self, to_place: ItemStack<T>) -> Result<(), StackErr<T>> {
51		if let Some(inner) = &mut self.inner {
52			match inner.stack(to_place) {
53				Ok(()) => Ok(()),
54				Err(rest) => Err(rest),
55			}
56		} else {
57			//protects from ItemStacks being amount 0
58			if to_place.get_amount() > 0 {
59				match ItemStack::new_from_stack(to_place) {
60					Ok(new) => {
61						self.inner = Some(new);
62						Ok(())
63					},
64					Err((new, rest)) => {
65						self.inner = Some(new);
66						Err(rest)
67					},
68				}
69			} else {
70				Ok(())
71			}
72		}
73	}
74
75	/// Returns the optional ItemStack in the Slot
76	pub fn inner(&self) -> &Option<ItemStack<T>> {
77		&self.inner
78	}
79
80	/// Returns the item in the Slot
81	pub fn get_item(&self) -> Result<&T, InvAccessErr> {
82		if let Some(inner) = &self.inner {
83			Ok(inner.get_item())
84		} else {
85			Err(InvAccessErr::SlotEmpty)
86		}
87	}
88
89	/// Returns the amount of items in the Slot
90	pub fn get_amount(&self) -> Result<usize, InvAccessErr> {
91		if let Some(inner) = &self.inner {
92			Ok(inner.get_amount())
93		} else {
94			Err(InvAccessErr::SlotEmpty)
95		}
96	}
97
98	/// Decreases amount by 1
99	pub fn decrease_amount(&mut self) -> Result<(), InvAccessErr> {
100		if let Some(inner) = &mut self.inner {
101			if inner.get_amount() > 0 {
102				inner.amount -= 1;
103				if inner.amount == 0 {
104					self.inner = None;
105				}
106				Ok(())
107			} else {
108				unreachable!();
109				//Err(InvAccessErr::AmountInsufficient)
110			}
111		} else {
112			Err(InvAccessErr::SlotEmpty)
113		}
114	}
115
116	/// Decreases amount by a given arbitrary number
117	pub fn decrease_amount_by(&mut self, amount: usize) -> Result<(), InvAccessErr> {
118		if let Some(inner) = &mut self.inner {
119			if inner.get_amount() >= amount {
120				inner.amount -= amount;
121				if inner.amount == 0 {
122					self.inner = None;
123				}
124				Ok(())
125			} else {
126				Err(InvAccessErr::AmountInsufficient)
127			}
128		} else {
129			Err(InvAccessErr::SlotEmpty)
130		}
131	}
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
135pub struct ItemStack<T> {
136	item: T,
137	amount: usize
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
141pub enum StackErr<T>
142where T: Stacksize + Eq + Clone {
143	ItemTypeDoesNotMatch(ItemStack<T>),
144	StackSizeOverflow(ItemStack<T>)
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
148pub enum InvAccessErr {
149	SlotOutOfBounds,
150	SlotEmpty,
151	ItemNotFound,
152	AmountInsufficient
153}
154
155pub type InvOverflow<T> = ItemStack<T>;
156
157impl<T> From<StackErr<T>> for ItemStack<T>
158where T: Stacksize + Eq + Clone {
159	fn from(item: StackErr<T>) -> Self {
160		match item {
161			StackErr::ItemTypeDoesNotMatch(x) => x,
162			StackErr::StackSizeOverflow(x) => x,
163		}
164	}
165}
166
167impl<T> ItemStack<T> 
168where T: Stacksize + Eq + Clone + Ord {
169	pub fn new(item: T, amount: usize) -> Self {
170		Self {
171			item,
172			amount
173		}
174	}
175
176	pub fn new_from_stack(mut new: ItemStack<T>) -> Result<Self, (Self, StackErr<T>)> {
177		match new.stacksize_split() {
178			Ok(()) => Ok(new),
179			Err(rest) => Err((new, StackErr::StackSizeOverflow(rest)))
180		}
181	}
182
183	fn stacksize_split(&mut self) -> Result<(), ItemStack<T>> {
184		let max = self.item.get_max_stacksize();
185		if self.amount > max {
186			let rest = self.amount - max;
187			self.amount = max;
188			Err(
189				ItemStack::<T>::new(
190				self.item.clone(), 
191				rest
192			))
193		} else {
194			Ok(())
195		}
196	}
197
198	pub fn stack(&mut self, other: ItemStack<T>) -> Result<(), StackErr<T>> {
199		if other.item == self.item {
200			self.amount += other.amount;
201			self.stacksize_split().map_err(|err| StackErr::StackSizeOverflow(err))
202		} else {
203			Err(StackErr::ItemTypeDoesNotMatch(other))
204		}
205	}
206
207	pub fn get_item(&self) -> &T {
208		&self.item
209	}
210
211	pub fn get_amount(&self) -> usize {
212		self.amount
213	}
214}
215
216impl<T> Inv<T> 
217where T: Stacksize + Eq + Clone + Ord {
218	pub fn new(slot_amount: usize) -> Self {
219		Inv {
220			slots: vec![Slot::new_empty(); slot_amount]
221		}
222	}
223
224	// Fill filled slots only
225	fn auto_stack_inner_filled(&mut self, to_place: ItemStack<T>) -> Result<(), StackErr<T>> {
226		let mut state = to_place.clone();
227		for slot in self.slots.iter_mut() {
228			if !slot.is_empty() {
229				match slot.stack(state) {
230					Ok(()) => return Ok(()),
231					Err(rest) => {
232						state = rest.into();
233					}
234				}
235			} else {
236				continue
237			}
238		}
239		Err(StackErr::StackSizeOverflow(state))
240	}
241
242	// Fill empty slots only
243	fn auto_stack_inner_empty(&mut self, to_place: ItemStack<T>) -> Result<(), StackErr<T>> {
244		let mut state = to_place.clone();
245		for slot in self.slots.iter_mut() {
246			if slot.is_empty() {
247				match slot.stack(state) {
248					Ok(()) => return Ok(()),
249					Err(rest) => {
250						state = rest.into();
251					}
252				}
253			}
254		}
255		Err(StackErr::StackSizeOverflow(state))
256	}
257
258	/// Add items to the Inventory
259	/// Already used slots will be filled before empty slots will
260	pub fn auto_stack(&mut self, to_place: ItemStack<T>) -> Result<(), InvOverflow<T>> {
261		match self.auto_stack_inner_filled(to_place) {
262			Ok(()) => return Ok(()),
263			Err(rest) => {
264				match self.auto_stack_inner_empty(rest.into()) {
265					Ok(()) => return Ok(()),
266					Err(rest) => Err(rest.into()),
267				}
268			}
269		}
270	}
271
272	/// Add items to a specific Slot
273	pub fn stack_at(&mut self, index: usize, to_place: ItemStack<T>) -> Result<Result<(), StackErr<T>>, InvAccessErr> {
274		match self.slots.get_mut(index) {
275			Some(slot) => {
276				Ok(
277					match slot.stack(to_place) {
278						Ok(()) => Ok(()),
279						Err(rest) => Err(rest),
280					}
281				)
282			},
283			None => Err(InvAccessErr::SlotOutOfBounds)
284		}
285	}
286
287	/// Take the entire ItemStack sitting in a Slot at a given position.
288	/// This means, that the ItemStack will be taken out of the slot, leaving it empty 
289	pub fn take_stack(&mut self, index: usize) -> Result<ItemStack<T>, InvAccessErr> {
290		match self.slots.get_mut(index) {
291			Some(slot) => {
292				if let Some(filled) = &slot.inner {
293					let take = filled.clone();
294					slot.inner = None;
295					Ok(take)
296				} else {
297					Err(InvAccessErr::SlotEmpty)
298				}
299			},
300			None => Err(InvAccessErr::SlotOutOfBounds)
301		}
302	}
303
304	/// Return a Slot with at a given position
305	pub fn get_slot(&self, index: usize) -> Result<&Slot<T>, InvAccessErr> {
306		match self.slots.get(index) {
307			Some(slot) => Ok(slot),
308			None => Err(InvAccessErr::SlotOutOfBounds)
309		}
310	}
311
312	/// Return a Slot with at a given position mutably
313	pub fn get_slot_mut(&mut self, index: usize) -> Result<&mut Slot<T>, InvAccessErr> {
314		match self.slots.get_mut(index) {
315			Some(slot) => Ok(slot),
316			None => Err(InvAccessErr::SlotOutOfBounds)
317		}
318	}
319
320	/// Return a Slot with a given item
321	pub fn find_slot(&self, item: T) -> Result<&Slot<T>, InvAccessErr> {
322		for slot in self.slots.iter() {
323			if let Some(inner) = &slot.inner {
324				if *inner.get_item() == item {
325					return Ok(slot);
326				}
327			}
328		}
329		Err(InvAccessErr::ItemNotFound)
330	}
331
332	/// Return a Slot with a given item mutably
333	pub fn find_slot_mut(&mut self, item: T) -> Result<&mut Slot<T>, InvAccessErr> {
334		for slot in self.slots.iter_mut() {
335			if let Some(inner) = &slot.inner {
336				if *inner.get_item() == item {
337					return Ok(slot);
338				}
339			}
340		}
341		Err(InvAccessErr::ItemNotFound)
342	}
343
344	/// Sort the Inventory
345	pub fn sort(&mut self) {
346		self.slots.sort_unstable();
347	}
348}
349
350#[cfg(test)]
351mod test;