use std::fmt; use std::iter::{Enumerate, FusedIterator}; use std::marker::PhantomData; use std::slice; use crate::item; #[derive(Clone, Debug, Eq)] /// stores data pub struct Storage { base: Vec, total: u64, holds: PhantomData, } pub type ItemStorage = Storage; impl Default for Storage { fn default() -> Self { Self { base: Vec::default(), total: 0, holds: PhantomData, } } } impl Storage where u16: From, { #[must_use] /// create a new storage /// /// ``` /// # use mindus::item::storage::ItemStorage; /// // ItemStorage is a alias to Storage /// let s = ItemStorage::new(); /// ``` pub fn new() -> Self { Self::default() } #[must_use] /// total items pub const fn get_total(&self) -> u64 { self.total } #[must_use] /// check if its empty /// /// ``` /// # use mindus::item::storage::ItemStorage; /// # use mindus::item; /// /// let mut s = ItemStorage::new(); /// assert!(s.is_empty()); /// s.set(item::Type::Copper, 500); /// assert!(!s.is_empty()); /// s.sub(item::Type::Copper, 500, 0); /// assert!(s.is_empty()); /// ``` pub const fn is_empty(&self) -> bool { self.total == 0 } /// get item count of certain element /// /// ``` /// # use mindus::item::storage::ItemStorage; /// # use mindus::item; /// /// let mut s = ItemStorage::new(); /// assert!(s.get(item::Type::Coal) == 0); /// s.set(item::Type::Coal, 500); /// assert!(s.get(item::Type::Titanium) == 0); /// assert!(s.get(item::Type::Coal) == 500); /// s.sub(item::Type::Coal, 500, 0); /// assert!(s.get(item::Type::Coal) == 0); /// ``` #[must_use] pub fn get(&self, ty: T) -> u32 { self.base.get(u16::from(ty) as usize).copied().unwrap_or(0) } /// set item count of certain element /// /// ``` /// # use mindus::item::storage::ItemStorage; /// # use mindus::item; /// /// let mut s = ItemStorage::new(); /// s.set(item::Type::Coal, 500); /// s.set(item::Type::Copper, 500); /// assert!(s.get(item::Type::Copper) == 500); /// ``` pub fn set(&mut self, ty: T, count: u32) -> u32 { let idx = u16::from(ty) as usize; match self.base.get_mut(idx) { None => { self.base.resize(idx + 1, 0); self.base[idx] = count; self.total += u64::from(count); 0 } Some(curr) => { let prev = *curr; self.total = self.total - u64::from(prev) + u64::from(count); *curr = count; prev } } } /// add to a certain elements item count, capping. /// /// ``` /// # use mindus::item::storage::ItemStorage; /// # use mindus::item; /// /// let mut s = ItemStorage::new(); /// s.add(item::Type::Coal, 500, 500); /// assert!(s.get(item::Type::Coal) == 500); /// s.add(item::Type::Coal, 500, 10000); /// assert!(s.get(item::Type::Coal) == 1000); /// s.add(item::Type::Coal, 500, 1250); /// assert!(s.get(item::Type::Coal) == 1250); /// ``` pub fn add(&mut self, ty: T, add: u32, max: u32) -> (u32, u32) { let idx = u16::from(ty) as usize; match self.base.get_mut(idx) { None => { let actual = add.min(max); self.base.resize(idx + 1, 0); self.base[idx] = actual; self.total += u64::from(add); (actual, actual) } Some(curr) => { if *curr < max { let actual = add.min(max - *curr); *curr += actual; self.total += u64::from(actual); (actual, *curr) } else { (0, *curr) } } } } /// like [`Storage::add`] but fails pub fn try_add(&mut self, ty: T, add: u32, max: u32) -> Result<(u32, u32), TryAddError> { let idx = u16::from(ty) as usize; match self.base.get_mut(idx) { None => { if add <= max { self.base.resize(idx + 1, 0); self.base[idx] = add; self.total += u64::from(add); Ok((add, add)) } else { Err(TryAddError { have: 0, add, max }) } } Some(curr) => { if *curr <= max && max - *curr <= add { *curr += add; self.total += u64::from(add); Ok((add, *curr)) } else { Err(TryAddError { have: *curr, add, max, }) } } } } pub fn sub(&mut self, ty: T, sub: u32, min: u32) -> (u32, u32) { match self.base.get_mut(u16::from(ty) as usize) { None => (0, 0), Some(curr) => { if *curr > min { let actual = sub.min(*curr - min); *curr -= actual; self.total -= u64::from(actual); (actual, *curr) } else { (0, *curr) } } } } pub fn try_sub(&mut self, ty: T, sub: u32, min: u32) -> Result<(u32, u32), TrySubError> { let idx = u16::from(ty) as usize; match self.base.get_mut(idx) { None => Err(TrySubError { have: 0, sub, min }), Some(curr) => { if *curr >= min && *curr - min >= sub { *curr -= sub; self.total -= u64::from(sub); Ok((sub, *curr)) } else { Err(TrySubError { have: *curr, sub, min, }) } } } } pub fn add_all(&mut self, other: &Storage, max_each: u32) -> (u64, u64) { let mut added = 0u64; if max_each > 0 && other.total > 0 { let mut iter = other.base.iter().enumerate(); // resize our vector only once and if necessary let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap(); if self.base.len() <= last { self.base.resize(last + 1, 0); } // process items by increasing ID for (idx, add) in iter { let curr = self.base[idx]; if curr < max_each { let actual = (*add).min(max_each - curr); self.base[idx] += actual; added += u64::from(actual); } } // process the final element (which we've retrieved first) let curr = self.base[last]; if curr < max_each { let actual = (*add_last).min(max_each - curr); self.base[last] += actual; added += u64::from(actual); } // update total self.total += added; } (added, self.total) } pub fn pull_all(&mut self, other: &mut Storage, max_each: u32) -> (u64, u64, u64) { let mut added = 0u64; if max_each > 0 && other.total > 0 { let mut iter = other.base.iter_mut().enumerate(); // resize our vector only once and if necessary let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap(); if self.base.len() <= last { self.base.resize(last + 1, 0); } // process items by increasing ID for (idx, add) in iter { let curr = self.base[idx]; if curr < max_each { let actual = (*add).min(max_each - curr); self.base[idx] += actual; *add -= actual; added += u64::from(actual); } } // process the final element (which we've retrieved first) let curr = self.base[last]; if curr < max_each { let actual = (*add_last).min(max_each - curr); self.base[last] += actual; *add_last -= actual; added += u64::from(actual); } // update totals self.total += added; other.total -= added; } (added, self.total, other.total) } pub fn sub_all(&mut self, other: &Storage, min_each: u32) -> (u64, u64) { let mut subbed = 0u64; if self.total > 0 && other.total > 0 { // no need for resizing, we only remove // process items by increasing ID for (idx, sub) in other.base.iter().enumerate() { if let Some(curr) = self.base.get(idx) { if *curr > min_each { let actual = (*sub).min(*curr - min_each); self.base[idx] -= actual; subbed += u64::from(actual); } } else { break; } } // update total self.total -= subbed; } (subbed, self.total) } pub fn diff_all(&mut self, other: &mut Storage, min_each: u32) -> (u64, u64, u64) { let mut subbed = 0u64; if self.total > 0 && other.total > 0 { // no need for resizing, we only remove // consider only indexes present in both let end = self.base.len().min(other.base.len()); let lhs = &mut self.base[..end]; let rhs = &mut other.base[..end]; // process items by increasing ID for (l, r) in lhs.iter_mut().zip(rhs) { if *l > min_each && *r > min_each { let actual = (*l - min_each).min(*r - min_each); *l -= actual; *r -= actual; subbed -= u64::from(actual); } } // update totals self.total -= subbed; other.total -= subbed; } (subbed, self.total, other.total) } #[must_use] pub fn iter(&self) -> Iter<'_> { Iter { base: self.base.iter().enumerate(), all: true, } } #[must_use] pub fn iter_nonzero(&self) -> Iter<'_> { Iter { base: self.base.iter().enumerate(), all: false, } } pub fn clear(&mut self) { self.base.clear(); } } // manual because padding with zeros doesn't affect equality impl PartialEq for Storage { fn eq(&self, other: &Self) -> bool { let mut li = self.base.iter().fuse(); let mut ri = other.base.iter().fuse(); loop { match (li.next(), ri.next()) { (None, None) => return true, (l, r) => { if l.unwrap_or(&0) != r.unwrap_or(&0) { return false; } } } } } } impl fmt::Display for Storage where u16: From, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut iter = self.iter_nonzero(); if let Some((ty, cnt)) = iter.next() { write!(f, "{cnt} {ty}")?; for (ty, cnt) in iter { write!(f, ", {cnt} {ty}")?; } } Ok(()) } } #[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)] #[error("adding {add} to current {have} would exceed {max}")] pub struct TryAddError { pub have: u32, pub add: u32, pub max: u32, } #[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)] #[error("removing {sub} from current {have} would drop below {min}")] pub struct TrySubError { pub have: u32, pub sub: u32, pub min: u32, } #[derive(Clone, Debug)] pub struct Iter<'l> { base: Enumerate>, all: bool, } impl<'l> Iterator for Iter<'l> { type Item = (item::Type, u32); fn next(&mut self) -> Option { for (idx, cnt) in self.base.by_ref() { if *cnt > 0 || self.all { if let Ok(ty) = item::Type::try_from(idx as u16) { return Some((ty, *cnt)); } } } None } fn size_hint(&self) -> (usize, Option) { (0, self.base.size_hint().1) } } impl<'l> FusedIterator for Iter<'l> where Enumerate>: FusedIterator {}