fish_lib/game/services/
item_service.rs

1use 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}