fish_lib/models/
item.rs

1use crate::config::ConfigInterface;
2use crate::data::item_data::ItemData;
3use crate::game::errors::item_event::GameItemEventError;
4use crate::models::item::attributes_container::ItemAttributesContainerInterface;
5use crate::models::item::properties::{ItemProperties, ItemPropertiesType};
6use crate::models::item::properties_container::{
7    ItemPropertiesContainer, ItemPropertiesContainerInterface,
8};
9use crate::traits::model::Model;
10use chrono::{DateTime, Utc};
11use diesel::{AsChangeset, Insertable, Queryable, Selectable};
12use serde::{Deserialize, Serialize};
13use std::collections::{HashMap, HashSet};
14use std::sync::Arc;
15
16pub mod attributes;
17pub mod attributes_container;
18pub mod properties;
19pub mod properties_container;
20
21#[derive(
22    Debug, Default, Serialize, Deserialize, Clone, PartialEq, Queryable, Selectable, AsChangeset,
23)]
24#[diesel(table_name = crate::schema::fish_items)]
25pub struct Item {
26    pub id: i64,
27    pub user_id: i64,
28    pub type_id: i32,
29    pub properties: ItemPropertiesContainer,
30    pub created_at: DateTime<Utc>,
31    pub updated_at: DateTime<Utc>,
32}
33
34impl Model for Item {
35    type Table = crate::schema::fish_items::table;
36    type PrimaryKeyType = i64;
37    type InsertType = NewItem;
38
39    fn table() -> Self::Table {
40        crate::schema::fish_items::table
41    }
42
43    fn id(&self) -> Self::PrimaryKeyType {
44        self.id
45    }
46}
47
48#[derive(Insertable)]
49#[diesel(table_name = crate::schema::fish_items)]
50#[diesel(check_for_backend(diesel::pg::Pg))]
51pub struct NewItem {
52    pub user_id: i64,
53    pub type_id: i32,
54    pub properties: ItemPropertiesContainer,
55}
56
57impl NewItem {
58    pub fn new(user_id: i64, item_data: Arc<ItemData>) -> Self {
59        Self {
60            user_id,
61            type_id: item_data.id,
62            properties: item_data.default_properties.clone(),
63        }
64    }
65}
66
67pub type ItemEventResult = Result<ItemEventSuccess, GameItemEventError>;
68
69#[derive(Debug, Default, Clone, PartialEq)]
70pub struct ItemEventSuccess {
71    pub consume: bool,
72}
73
74impl ItemEventSuccess {
75    pub fn new(consume: bool) -> Self {
76        Self { consume }
77    }
78}
79
80impl Item {
81    pub fn migrate_properties(&mut self, config: Arc<dyn ConfigInterface>) -> ItemEventResult {
82        let item_data = config
83            .get_item_data(self.type_id)
84            .ok_or(GameItemEventError::invalid_item_type(self.type_id))?;
85
86        let required_property_types: HashSet<ItemPropertiesType> =
87            item_data.default_properties.get_properties_types();
88
89        let existing_property_types: HashSet<ItemPropertiesType> =
90            self.properties.get_properties_types();
91
92        let properties_to_add: HashSet<ItemPropertiesType> = required_property_types
93            .difference(&existing_property_types)
94            .copied()
95            .collect();
96
97        let properties_to_remove: HashSet<ItemPropertiesType> = existing_property_types
98            .difference(&required_property_types)
99            .copied()
100            .collect();
101
102        properties_to_add.iter().for_each(|component_type| {
103            let component = component_type.get_default_properties();
104            self.properties.add_properties(component);
105        });
106
107        properties_to_remove
108            .iter()
109            .for_each(|component_type| self.properties.remove_properties(*component_type));
110
111        Ok(ItemEventSuccess::new(self.should_consume()))
112    }
113
114    pub fn use_as_rod(&mut self, config: Arc<dyn ConfigInterface>) -> ItemEventResult {
115        let attributes = self
116            .attributes(config)
117            .ok_or(GameItemEventError::invalid_item_type(self.type_id))?;
118
119        if !attributes.is_rod() {
120            Err(GameItemEventError::not_a_rod(self.type_id))
121        } else {
122            self.properties.on_use(1);
123            Ok(ItemEventSuccess::new(self.should_consume()))
124        }
125    }
126
127    pub fn add(&mut self, amount: u64) -> ItemEventResult {
128        self.on_add(amount);
129        Ok(ItemEventSuccess::new(self.should_consume()))
130    }
131
132    pub fn remove(&mut self, amount: u64) -> ItemEventResult {
133        self.on_remove(amount);
134        Ok(ItemEventSuccess::new(self.should_consume()))
135    }
136}
137
138pub trait ItemInterface: ItemPropertiesContainerInterface {
139    fn attributes(
140        &self,
141        config: Arc<dyn ConfigInterface>,
142    ) -> Option<Arc<dyn ItemAttributesContainerInterface>>;
143}
144
145impl ItemInterface for Item {
146    fn attributes(
147        &self,
148        config: Arc<dyn ConfigInterface>,
149    ) -> Option<Arc<dyn ItemAttributesContainerInterface>> {
150        config
151            .get_item_data(self.type_id)
152            .map(|data| data as Arc<dyn ItemAttributesContainerInterface>)
153    }
154}
155
156impl ItemPropertiesContainerInterface for Item {
157    fn get_properties(&self) -> &HashMap<ItemPropertiesType, ItemProperties> {
158        self.properties.get_properties()
159    }
160
161    fn get_properties_mut(&mut self) -> &mut HashMap<ItemPropertiesType, ItemProperties> {
162        self.properties.get_properties_mut()
163    }
164}