1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
5#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
6#[serde(default)]
7pub struct Pool {
8 pub max: u16,
9 pub used: u16,
10}
11
12impl Pool {
13 pub fn new(max: u16) -> Self {
15 Self { max, used: 0 }
16 }
17
18 pub fn remaining(&self) -> u16 {
20 self.max.saturating_sub(self.used)
21 }
22
23 pub fn expend(&mut self, amount: u16) -> bool {
26 if amount > self.remaining() {
27 return false;
28 }
29 self.used += amount;
30 true
31 }
32
33 pub fn restore(&mut self, amount: u16) {
35 self.used = self.used.saturating_sub(amount);
36 }
37
38 pub fn reset(&mut self) {
40 self.used = 0;
41 }
42
43 pub fn is_valid(&self) -> bool {
45 self.used <= self.max
46 }
47}
48
49#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
51#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
52#[serde(rename_all = "snake_case")]
53pub enum Recharge {
54 ShortRest,
56 #[default]
58 LongRest,
59 Manual,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
65#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
66#[serde(rename_all = "snake_case")]
67pub enum RestKind {
68 Short,
69 Long,
70}
71
72impl Recharge {
73 pub fn recovers_on(self, rest: RestKind) -> bool {
75 match self {
76 Recharge::ShortRest => true,
77 Recharge::LongRest => rest == RestKind::Long,
78 Recharge::Manual => false,
79 }
80 }
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
86pub struct Resource {
87 pub name: String,
88 #[serde(flatten)]
89 pub uses: Pool,
90 #[serde(default)]
91 pub recharge: Recharge,
92 #[serde(default, skip_serializing_if = "String::is_empty")]
93 pub notes: String,
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn pool_never_overspends_or_overrestores() {
102 let mut p = Pool::new(2);
103 assert!(p.expend(2));
104 assert!(!p.expend(1));
105 assert_eq!(p.used, 2);
106 p.restore(5);
107 assert_eq!(p.used, 0);
108 }
109
110 #[test]
111 fn recharge_rules() {
112 assert!(Recharge::ShortRest.recovers_on(RestKind::Short));
113 assert!(Recharge::ShortRest.recovers_on(RestKind::Long));
114 assert!(!Recharge::LongRest.recovers_on(RestKind::Short));
115 assert!(!Recharge::Manual.recovers_on(RestKind::Long));
116 }
117}