fish_lib/models/item/
properties_container.rs

1use crate::models::item::properties::stackable::StackableComponent;
2use crate::models::item::properties::usage_count::UsageComponent;
3use crate::models::item::properties::{ItemProperties, ItemPropertiesType};
4use diesel::deserialize::FromSql;
5use diesel::pg::Pg;
6use diesel::serialize::{Output, ToSql};
7use diesel::sql_types::Jsonb;
8use diesel::{deserialize, serialize, AsExpression, FromSqlRow};
9use serde::{Deserialize, Serialize};
10use std::collections::{HashMap, HashSet};
11
12#[derive(Debug, Default, Clone, PartialEq, Serialize, FromSqlRow, AsExpression)]
13#[diesel(sql_type = diesel::sql_types::Jsonb)]
14pub struct ItemPropertiesContainer {
15    components: HashMap<ItemPropertiesType, ItemProperties>,
16}
17
18impl ItemPropertiesContainer {
19    pub fn new() -> Self {
20        Self {
21            components: HashMap::new(),
22        }
23    }
24
25    pub fn get_properties_types(&self) -> HashSet<ItemPropertiesType> {
26        self.components.keys().copied().collect()
27    }
28
29    pub fn add_properties(&mut self, properties: ItemProperties) {
30        match properties {
31            ItemProperties::Stackable(_) => self
32                .components
33                .insert(ItemPropertiesType::Stackable, properties),
34            ItemProperties::Usage(_) => self
35                .components
36                .insert(ItemPropertiesType::Usage, properties),
37        };
38    }
39
40    pub fn remove_properties(&mut self, properties_type: ItemPropertiesType) {
41        self.components.remove(&properties_type);
42    }
43
44    pub fn with_stackable(mut self, count: u64) -> Self {
45        let component = ItemProperties::stackable(count);
46        self.add_properties(component);
47        self
48    }
49
50    pub fn with_usage(mut self, count: u64) -> Self {
51        let component = ItemProperties::usage(count);
52        self.add_properties(component);
53        self
54    }
55}
56
57impl ItemPropertiesContainerInterface for ItemPropertiesContainer {
58    fn get_properties(&self) -> &HashMap<ItemPropertiesType, ItemProperties> {
59        &self.components
60    }
61
62    fn get_properties_mut(&mut self) -> &mut HashMap<ItemPropertiesType, ItemProperties> {
63        &mut self.components
64    }
65}
66
67pub trait ItemPropertiesContainerInterface {
68    fn get_properties(&self) -> &HashMap<ItemPropertiesType, ItemProperties>;
69    fn get_properties_mut(&mut self) -> &mut HashMap<ItemPropertiesType, ItemProperties>;
70
71    fn get_stackable_properties(&self) -> Option<&StackableComponent> {
72        match self.get_properties().get(&ItemPropertiesType::Stackable) {
73            Some(ItemProperties::Stackable(stackable)) => Some(stackable),
74            Some(_) | None => None,
75        }
76    }
77
78    fn get_stackable_properties_mut(&mut self) -> Option<&mut StackableComponent> {
79        match self
80            .get_properties_mut()
81            .get_mut(&ItemPropertiesType::Stackable)
82        {
83            Some(ItemProperties::Stackable(stackable)) => Some(stackable),
84            Some(_) | None => None,
85        }
86    }
87
88    fn get_usage_properties(&self) -> Option<&UsageComponent> {
89        match self.get_properties().get(&ItemPropertiesType::Usage) {
90            Some(ItemProperties::Usage(usage)) => Some(usage),
91            Some(_) | None => None,
92        }
93    }
94    fn get_usage_properties_mut(&mut self) -> Option<&mut UsageComponent> {
95        match self
96            .get_properties_mut()
97            .get_mut(&ItemPropertiesType::Usage)
98        {
99            Some(ItemProperties::Usage(usage)) => Some(usage),
100            Some(_) | None => None,
101        }
102    }
103
104    // Direct manipulation
105    fn set_count(&mut self, count: u64) {
106        self.get_stackable_properties_mut().map(|stackable| {
107            stackable.set_count(count);
108        });
109    }
110
111    // Properties-existence functions
112    fn is_stackable(&self) -> bool {
113        self.get_stackable_properties().is_some()
114    }
115
116    fn has_usage(&self) -> bool {
117        self.get_usage_properties().is_some()
118    }
119
120    // Properties-specific variable access
121    fn get_count(&self) -> Option<u64> {
122        self.get_stackable_properties()
123            .map(|stackable| stackable.get_count())
124    }
125
126    fn get_times_used(&self) -> Option<u64> {
127        self.get_usage_properties()
128            .map(|usage| usage.get_times_used())
129    }
130
131    // Properties events
132    fn on_use(&mut self, times: u64) {
133        self.get_properties_mut()
134            .values_mut()
135            .for_each(|component| component.on_use(times))
136    }
137
138    fn on_add(&mut self, amount: u64) {
139        self.get_properties_mut()
140            .values_mut()
141            .for_each(|component| component.on_add(amount));
142    }
143
144    fn on_remove(&mut self, amount: u64) {
145        self.get_properties_mut()
146            .values_mut()
147            .for_each(|component| component.on_remove(amount));
148    }
149
150    fn should_consume(&self) -> bool {
151        self.get_properties()
152            .values()
153            .any(|component| component.should_consume())
154    }
155}
156
157impl ToSql<Jsonb, Pg> for ItemPropertiesContainer {
158    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
159        let value = serde_json::to_value(self)?;
160        ToSql::<Jsonb, Pg>::to_sql(&value, &mut out.reborrow())
161    }
162}
163
164impl FromSql<Jsonb, Pg> for ItemPropertiesContainer {
165    fn from_sql(
166        bytes: <Pg as diesel::backend::Backend>::RawValue<'_>,
167    ) -> deserialize::Result<Self> {
168        let value = <serde_json::Value as FromSql<Jsonb, Pg>>::from_sql(bytes)?;
169        Ok(serde_json::from_value(value)?)
170    }
171}
172
173// Serde
174impl<'de> Deserialize<'de> for ItemPropertiesContainer {
175    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
176    where
177        D: serde::Deserializer<'de>,
178    {
179        #[derive(Deserialize)]
180        struct Helper {
181            components: HashMap<String, serde_json::Value>,
182        }
183
184        let helper = Helper::deserialize(deserializer)?;
185        let mut container = ItemPropertiesContainer::new();
186
187        for (type_str, component_value) in helper.components {
188            let (component_type, component) = match type_str.as_str() {
189                "Usage" => {
190                    let usage = serde_json::from_value(component_value)
191                        .map_err(serde::de::Error::custom)?;
192                    (ItemPropertiesType::Usage, ItemProperties::Usage(usage))
193                }
194                "Stackable" => {
195                    let stackable = serde_json::from_value(component_value)
196                        .map_err(serde::de::Error::custom)?;
197                    (
198                        ItemPropertiesType::Stackable,
199                        ItemProperties::Stackable(stackable),
200                    )
201                }
202                _ => continue,
203            };
204
205            container.components.insert(component_type, component);
206        }
207
208        Ok(container)
209    }
210}