serenity/utils/argument_convert/
role.rs

1use std::fmt;
2
3use super::ArgumentConvert;
4use crate::model::prelude::*;
5use crate::prelude::*;
6
7/// Error that can be returned from [`Role::convert`].
8#[non_exhaustive]
9#[derive(Debug)]
10pub enum RoleParseError {
11    /// When the operation was invoked outside a guild.
12    NotInGuild,
13    /// When the guild's roles were not found in cache.
14    NotInCache,
15    /// HTTP error while retrieving guild roles.
16    Http(SerenityError),
17    /// The provided channel string failed to parse, or the parsed result cannot be found in the
18    /// cache.
19    NotFoundOrMalformed,
20}
21
22impl std::error::Error for RoleParseError {
23    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
24        match self {
25            Self::Http(e) => Some(e),
26            Self::NotFoundOrMalformed | Self::NotInGuild | Self::NotInCache => None,
27        }
28    }
29}
30
31impl fmt::Display for RoleParseError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Self::NotInGuild => f.write_str("Must invoke this operation in a guild"),
35            Self::NotInCache => f.write_str("Guild's roles were not found in cache"),
36            Self::Http(_) => f.write_str("Failed to retrieve roles via HTTP"),
37            Self::NotFoundOrMalformed => f.write_str("Role not found or unknown format"),
38        }
39    }
40}
41
42/// Look up a [`Role`] by a string case-insensitively.
43///
44/// Requires the cache feature to be enabled.
45///
46/// The lookup strategy is as follows (in order):
47/// 1. Lookup by ID
48/// 2. [Lookup by mention](`crate::utils::parse_role`).
49/// 3. Lookup by name (case-insensitive)
50#[async_trait::async_trait]
51impl ArgumentConvert for Role {
52    type Err = RoleParseError;
53
54    async fn convert(
55        ctx: impl CacheHttp,
56        guild_id: Option<GuildId>,
57        _channel_id: Option<ChannelId>,
58        s: &str,
59    ) -> Result<Self, Self::Err> {
60        let guild_id = guild_id.ok_or(RoleParseError::NotInGuild)?;
61
62        #[cfg(feature = "cache")]
63        let guild;
64
65        #[cfg(feature = "cache")]
66        let roles = {
67            let cache = ctx.cache().ok_or(RoleParseError::NotInCache)?;
68            guild = cache.guild(guild_id).ok_or(RoleParseError::NotInCache)?;
69            &guild.roles
70        };
71
72        #[cfg(not(feature = "cache"))]
73        let roles = ctx.http().get_guild_roles(guild_id).await.map_err(RoleParseError::Http)?;
74
75        if let Some(role_id) = s.parse().ok().or_else(|| crate::utils::parse_role_mention(s)) {
76            #[cfg(feature = "cache")]
77            if let Some(role) = roles.get(&role_id) {
78                return Ok(role.clone());
79            }
80            #[cfg(not(feature = "cache"))]
81            if let Some(role) = roles.iter().find(|role| role.id == role_id) {
82                return Ok(role.clone());
83            }
84        }
85
86        #[cfg(feature = "cache")]
87        if let Some(role) = roles.values().find(|role| role.name.eq_ignore_ascii_case(s)) {
88            return Ok(role.clone());
89        }
90        #[cfg(not(feature = "cache"))]
91        if let Some(role) = roles.into_iter().find(|role| role.name.eq_ignore_ascii_case(s)) {
92            return Ok(role);
93        }
94
95        Err(RoleParseError::NotFoundOrMalformed)
96    }
97}