fish_lib/game/services/
item_service.rs1use crate::config::ConfigInterface;
2use crate::data::item_data::ItemData;
3use crate::dto::inventory::Inventory;
4use crate::game::errors::resource::GameResourceError;
5use crate::game::errors::GameResult;
6use crate::game::repositories::item_repository::ItemRepositoryInterface;
7use crate::models::item::properties_container::ItemPropertiesContainerInterface;
8use crate::models::item::{Item, ItemEventResult, ItemEventSuccess, NewItem};
9use crate::models::user::User;
10use std::sync::Arc;
11
12pub trait ItemServiceInterface: Send + Sync {
13 fn get_item_data(&self, type_id: i32) -> GameResult<Arc<ItemData>>;
14 fn add_new_item(&self, item: NewItem, user: &User) -> GameResult<Item>;
15 fn create_and_save_item(&self, item_data: Arc<ItemData>, user: &User) -> GameResult<Item>;
16 fn create_and_save_item_with_count(
17 &self,
18 item_data: Arc<ItemData>,
19 user: &User,
20 count: u64,
21 ) -> GameResult<Item>;
22 fn manipulate(
23 &self,
24 item: Item,
25 function: Box<dyn Fn(&mut Item) -> ItemEventResult>,
26 ) -> GameResult<ItemEventSuccess>;
27 fn get_inventory(&self, user: &User) -> GameResult<Inventory>;
28}
29
30pub struct ItemService {
31 config: Arc<dyn ConfigInterface>,
32 item_repository: Arc<dyn ItemRepositoryInterface>,
33}
34
35impl ItemService {
36 pub fn new(
37 config: Arc<dyn ConfigInterface>,
38 item_repository: Arc<dyn ItemRepositoryInterface>,
39 ) -> ItemService {
40 ItemService {
41 config,
42 item_repository,
43 }
44 }
45}
46
47impl ItemServiceInterface for ItemService {
48 fn get_item_data(&self, type_id: i32) -> GameResult<Arc<ItemData>> {
49 self.config
50 .get_item_data(type_id)
51 .ok_or(GameResourceError::item_not_found(type_id).into())
52 }
53
54 fn add_new_item(&self, new_item: NewItem, user: &User) -> GameResult<Item> {
55 let item_data = self.get_item_data(new_item.type_id)?;
56 let existing_items = self
57 .item_repository
58 .find_by_type_and_user(new_item.type_id, user.id)?;
59
60 let max_count = item_data.max_count;
61 let has_count = existing_items.len() as u32;
62
63 let count_max_exceeded = max_count > 1 && has_count >= max_count;
64 let count_unique_exceeded = max_count == 1 && has_count > 0 && !item_data.is_stackable();
65 if count_max_exceeded || count_unique_exceeded {
66 return Err(GameResourceError::item_max_count_exceeded(
67 new_item.type_id,
68 user.external_id,
69 )
70 .into());
71 };
72
73 let item = if max_count > 1 || has_count == 0 || !item_data.is_stackable() {
74 self.item_repository.create(new_item)?
75 } else {
76 let amount =
77 new_item
78 .properties
79 .get_count()
80 .ok_or(GameResourceError::item_unstackable(
81 new_item.type_id,
82 "New item has no count property",
83 ))?;
84
85 let mut item_to_edit =
86 existing_items
87 .first()
88 .cloned()
89 .ok_or(GameResourceError::item_unstackable(
90 new_item.type_id,
91 "Did not find any item to add the new item's amount to",
92 ))?;
93
94 item_to_edit.add(amount)?;
95 self.item_repository.save(item_to_edit)?
96 };
97
98 Ok(item)
99 }
100
101 fn create_and_save_item(&self, item_data: Arc<ItemData>, user: &User) -> GameResult<Item> {
102 let new_item = NewItem::new(user.id, item_data.clone());
103 self.add_new_item(new_item, user)
104 }
105
106 fn create_and_save_item_with_count(
107 &self,
108 item_data: Arc<ItemData>,
109 user: &User,
110 count: u64,
111 ) -> GameResult<Item> {
112 let mut new_item = NewItem::new(user.id, item_data.clone());
113 if !item_data.is_stackable() {
114 return Err(GameResourceError::item_unstackable(
115 item_data.id,
116 "count provided on item creation, but item is unstackable",
117 )
118 .into());
119 } else {
120 new_item.properties.set_count(count);
121 }
122 self.add_new_item(new_item, user)
123 }
124
125 fn manipulate(
126 &self,
127 mut item: Item,
128 function: Box<dyn Fn(&mut Item) -> ItemEventResult>,
129 ) -> GameResult<ItemEventSuccess> {
130 let success = function(&mut item)?;
131
132 if success.consume {
133 self.item_repository.delete(item)?;
134 } else {
135 self.item_repository.save(item)?;
136 }
137
138 Ok(success)
139 }
140
141 fn get_inventory(&self, user: &User) -> GameResult<Inventory> {
142 let items = self.item_repository.find_by_user(user.id)?;
143 Ok(Inventory::new(self.config.clone(), items))
144 }
145}