stat_summoner/module/randomchampions/utils.rs
1use futures::TryStreamExt;
2use mongodb::bson::doc;
3use rand::Rng;
4
5use crate::models::{
6 data::{ChampionData, Data},
7 error::Error,
8 role::Role,
9};
10
11/// ⚙️ Maps a `Role` enum value to its corresponding string representation as stored in the database.
12///
13/// This function takes a `Role` enum and converts it to a `String` that matches the format used in the database.
14/// It is used to ensure consistency between the role representation in the code and the role names stored in the database.
15///
16/// # Parameters:
17/// - `role`: A `Role` enum representing the role to be converted.
18///
19/// # Returns:
20/// - `String`: The corresponding string representation of the role (e.g., "Top", "Jungler").
21///
22/// # Example:
23/// ```rust
24/// let role = Role::TOPLANE;
25/// let role_str = match_role_with_database_roles(role);
26/// assert_eq!(role_str, "Top");
27/// ```
28///
29/// # ⚠️ Notes:
30/// - The function uses Rust's `match` control flow to convert each `Role` variant to the corresponding string.
31/// - This function is essential for ensuring compatibility between different parts of the code and the data storage layer.
32///
33/// # Related Enums:
34/// - `Role`: Represents different League of Legends roles like `TOPLANE`, `JUNGLE`, `MIDLANE`, etc.
35///
36/// # See Also:
37/// - `get_champions_by_role`: Uses the string representation of a role to query the database for champions with that role.
38fn match_role_with_database_roles(role: Role) -> String {
39 match role {
40 Role::TOPLANE => "Top".to_string(),
41 Role::JUNGLE => "Jungler".to_string(),
42 Role::MIDLANE => "Mid".to_string(),
43 Role::ADC => "AD Carry".to_string(),
44 Role::SUPPORT => "Support".to_string(),
45 }
46}
47
48/// ⚙️ Retrieves all champions that match the specified role from the database.
49///
50/// This asynchronous function queries a MongoDB collection to find all champions that include the specified role.
51/// The function returns a vector of `ChampionData` objects that match the role provided.
52///
53/// # Parameters:
54/// - `role`: A reference to a string representing the role to filter champions by (e.g., "Top", "Jungler").
55/// - `collection`: A reference to a MongoDB collection of `ChampionData` representing the champion data stored in the database.
56///
57/// # Returns:
58/// - `Result<Vec<ChampionData>, mongodb::error::Error>`: On success, returns a vector of `ChampionData` objects that match the role.
59/// On failure, returns an `Error` from MongoDB indicating what went wrong.
60///
61/// # Example:
62/// ```rust
63/// let role = "AD Carry";
64/// let champions = get_champions_by_role(role, &collection).await?;
65/// for champion in champions {
66/// println!("Champion: {}", champion.name);
67/// }
68/// ```
69///
70/// # ⚠️ Notes:
71/// - The function uses MongoDB's `$in` operator to search for champions whose roles include the specified value.
72/// - The result is collected into a vector using the `try_collect` method for convenience.
73///
74/// # Related Functions:
75/// - `match_role_with_database_roles`: Converts a `Role` enum into the corresponding string representation, which can be used as input for this function.
76///
77/// # Dependencies:
78/// - This function depends on a MongoDB collection that stores `ChampionData` documents.
79/// - Requires the `futures` crate for the `try_collect` method to handle the cursor results asynchronously.
80async fn get_champions_by_role(
81 role: &str,
82 collection: &mongodb::Collection<ChampionData>,
83) -> mongodb::error::Result<Vec<ChampionData>> {
84 let filter = doc! {
85 "role": {
86 "$in": [role]
87 }
88 };
89 let cursor = collection.find(filter).await?;
90
91 cursor.try_collect().await
92}
93
94/// ⚙️ Retrieves all champions from the database without filtering by role.
95///
96/// This asynchronous function queries a MongoDB collection to retrieve all `ChampionData` documents, regardless of their role.
97/// It is used to obtain a complete list of champions stored in the collection.
98///
99/// # Parameters:
100/// - `collection`: A reference to a MongoDB collection of `ChampionData` representing the champion data stored in the database.
101///
102/// # Returns:
103/// - `Result<Vec<ChampionData>, mongodb::error::Error>`: On success, returns a vector of all `ChampionData` objects in the collection.
104/// On failure, returns an `Error` from MongoDB detailing the issue.
105///
106/// # Example:
107/// ```rust
108/// let champions = get_champions_with_no_role(&collection).await?;
109/// for champion in champions {
110/// println!("Champion: {}", champion.name);
111/// }
112/// ```
113///
114/// # ⚠️ Notes:
115/// - The function uses an empty filter (`{}`) to retrieve all documents in the `ChampionData` collection.
116/// - The result is collected into a vector using the `try_collect` method, which allows for asynchronous processing of the cursor results.
117///
118/// # Related Functions:
119/// - `get_champions_by_role`: Retrieves champions filtered by a specific role from the collection.
120///
121/// # Dependencies:
122/// - This function depends on a MongoDB collection that stores `ChampionData` documents.
123/// - Requires the `futures` crate for the `try_collect` method to handle the cursor results asynchronously.
124async fn get_champions_with_no_role(
125 collection: &mongodb::Collection<ChampionData>,
126) -> mongodb::error::Result<Vec<ChampionData>> {
127 let filter = doc! {};
128 let cursor = collection.find(filter).await?;
129
130 cursor.try_collect().await
131}
132
133/// ⚙️ Selects a random champion from a list of `ChampionData`.
134///
135/// This function takes a vector of `ChampionData` and returns a random champion from that list.
136/// It uses a random number generator to choose an index, ensuring that each champion has an equal chance of being selected.
137///
138/// # Parameters:
139/// - `champions`: A `Vec<ChampionData>` containing the list of champions from which a random champion will be selected.
140///
141/// # Returns:
142/// - `ChampionData`: A clone of the randomly selected `ChampionData` object.
143///
144/// # Example:
145/// ```rust
146/// let champions_list = vec![champion1, champion2, champion3];
147/// let random_champion = get_random_champion(champions_list);
148/// println!("Selected Champion: {}", random_champion.name);
149/// ```
150///
151/// # ⚠️ Notes:
152/// - The function uses the `rand` crate to generate a random index, and selects the champion at that index.
153/// - The selected champion is cloned before being returned, ensuring the original vector remains unmodified.
154///
155/// # Related Functions:
156/// - `get_list_champions`: Retrieves a list of champions that can be passed to this function to select a random one.
157///
158/// # Dependencies:
159/// - Requires the `rand` crate for generating a random index.
160pub fn get_random_champion(champions: Vec<ChampionData>) -> ChampionData {
161 let mut rng = rand::rng();
162 let index = rng.random_range(0..champions.len());
163 let champion = &champions[index];
164 champion.clone()
165}
166
167/// ⚙️ Retrieves a list of champions from the database, optionally filtered by role.
168///
169/// This asynchronous function queries the MongoDB collection of champions to retrieve either all champions or those matching a specific role.
170/// If a role is provided, the function will filter the list accordingly. If no role is specified, all champions will be retrieved.
171///
172/// # Parameters:
173/// - `ctx`: The command context, providing access to the bot's data and other utilities.
174/// - `role`: An optional `Role` enum to filter the champions by role. If `None`, all champions are retrieved.
175///
176/// # Returns:
177/// - `Result<Vec<ChampionData>, Error>`: On success, returns a vector of `ChampionData` objects representing the champions that match the criteria.
178/// On failure, returns an `Error` indicating what went wrong during the database query.
179///
180/// # Example:
181/// ```rust
182/// let champions_list = get_list_champions(ctx, Some(Role::MIDLANE)).await?;
183/// for champion in champions_list {
184/// println!("Champion: {}", champion.name);
185/// }
186/// ```
187///
188/// # ⚠️ Notes:
189/// - If `role` is `None`, the function calls `get_champions_with_no_role` to retrieve all champions.
190/// - If `role` is provided, `match_role_with_database_roles` is used to convert the `Role` enum to a string, and `get_champions_by_role` is called to perform the filtered search.
191/// - This function interacts with MongoDB asynchronously and relies on other helper functions for specific queries.
192///
193/// # Related Functions:
194/// - `get_champions_with_no_role`: Retrieves all champions without filtering by role.
195/// - `get_champions_by_role`: Retrieves champions filtered by a specific role from the collection.
196/// - `match_role_with_database_roles`: Converts a `Role` enum into the corresponding string representation.
197///
198/// # Dependencies:
199/// - Requires access to a MongoDB collection (`champions_data`) for champion information.
200/// - The function depends on other helper functions for querying the MongoDB collection.
201pub async fn get_list_champions(
202 ctx: poise::ApplicationContext<'_, Data, Error>,
203 role: Option<Role>,
204) -> Result<Vec<ChampionData>, Error> {
205 let mongo_client = &ctx.data().mongo_client;
206 let collection = mongo_client
207 .database("stat-summoner")
208 .collection::<ChampionData>("champions_data");
209 if role.is_none() {
210 let champions = get_champions_with_no_role(&collection).await?;
211 return Ok(champions);
212 } else {
213 let role = match_role_with_database_roles(role.unwrap());
214 let champions = get_champions_by_role(&role, &collection).await?;
215 return Ok(champions);
216 }
217}