Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'src/item/storage.rs')
| -rw-r--r-- | src/item/storage.rs | 425 |
1 files changed, 0 insertions, 425 deletions
diff --git a/src/item/storage.rs b/src/item/storage.rs deleted file mode 100644 index ec7f219..0000000 --- a/src/item/storage.rs +++ /dev/null @@ -1,425 +0,0 @@ -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<T> { - base: Vec<u32>, - total: u64, - holds: PhantomData<T>, -} - -pub type ItemStorage = Storage<item::Type>; - -impl<T> Default for Storage<T> { - fn default() -> Self { - Self { - base: Vec::default(), - total: 0, - holds: PhantomData, - } - } -} - -impl<T> Storage<T> -where - u16: From<T>, -{ - #[must_use] - /// create a new storage - /// - /// ``` - /// # use mindus::item::storage::ItemStorage; - /// // ItemStorage is a alias to Storage<Item> - /// 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<T>, 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<T>, 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<T>, 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<T>, 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<T> PartialEq for Storage<T> { - 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<T> fmt::Display for Storage<T> -where - u16: From<T>, -{ - 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<slice::Iter<'l, u32>>, - all: bool, -} - -impl<'l> Iterator for Iter<'l> { - type Item = (item::Type, u32); - - fn next(&mut self) -> Option<Self::Item> { - 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<usize>) { - (0, self.base.size_hint().1) - } -} - -impl<'l> FusedIterator for Iter<'l> where Enumerate<slice::Iter<'l, u32>>: FusedIterator {} |