Skip to content

Commit

Permalink
Move Member::highest_role_info to Guild::member_highest_role (#2816)
Browse files Browse the repository at this point in the history
This means:
- `PartialGuild::greater_member_hierarchy` isn't a duplicate of
`Guild::greater_member_hierarchy` anymore
- `Guild::greater_member_hierarchy` doesn't need the cache parameter, and
this can be removed in a followup PR to next.
- `Guild::member_highest_role` doesn't need cache and can return back a
reference to the role, instead of id and position which then required
another lookup.
  • Loading branch information
GnomedDev authored Mar 25, 2024
1 parent 5fc5acf commit d29962d
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 94 deletions.
27 changes: 7 additions & 20 deletions src/model/guild/member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,27 +283,14 @@ impl Member {
/// The "highest role in hierarchy" is defined as the role with the highest position. If two or
/// more roles have the same highest position, then the role with the lowest ID is the highest.
#[cfg(feature = "cache")]
#[deprecated = "Use Guild::member_highest_role"]
pub fn highest_role_info(&self, cache: impl AsRef<Cache>) -> Option<(RoleId, u16)> {
let guild = cache.as_ref().guild(self.guild_id)?;

let mut highest = None;

for role_id in &self.roles {
if let Some(role) = guild.roles.get(role_id) {
// Skip this role if this role in iteration has:
// - a position less than the recorded highest
// - a position equal to the recorded, but a higher ID
if let Some((id, pos)) = highest {
if role.position < pos || (role.position == pos && role.id > id) {
continue;
}
}

highest = Some((role.id, role.position));
}
}

highest
cache
.as_ref()
.guild(self.guild_id)
.as_ref()
.and_then(|g| g.member_highest_role(self))
.map(|r| (r.id, r.position))
}

/// Kick the member from the guild.
Expand Down
49 changes: 37 additions & 12 deletions src/model/guild/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,33 @@ impl Guild {
guild_id.into().to_partial_guild(cache_http).await
}

/// Gets the highest role a [`Member`] of this Guild has.
///
/// Returns None if the member has no roles or the member from this guild.
#[must_use]
pub fn member_highest_role(&self, member: &Member) -> Option<&Role> {
let mut highest: Option<&Role> = None;

for role_id in &member.roles {
if let Some(role) = self.roles.get(role_id) {
// Skip this role if this role in iteration has:
// - a position less than the recorded highest
// - a position equal to the recorded, but a higher ID
if let Some(highest) = highest {
if role.position < highest.position
|| (role.position == highest.position && role.id > highest.id)
{
continue;
}
}

highest = Some(role);
}
}

highest
}

/// Returns which of two [`User`]s has a higher [`Member`] hierarchy.
///
/// Hierarchy is essentially who has the [`Role`] with the highest [`position`].
Expand All @@ -1368,20 +1395,15 @@ impl Guild {
#[inline]
pub fn greater_member_hierarchy(
&self,
cache: impl AsRef<Cache>,
#[allow(unused_variables)] _cache: impl AsRef<Cache>,
lhs_id: impl Into<UserId>,
rhs_id: impl Into<UserId>,
) -> Option<UserId> {
self._greater_member_hierarchy(&cache, lhs_id.into(), rhs_id.into())
self._greater_member_hierarchy(lhs_id.into(), rhs_id.into())
}

#[cfg(feature = "cache")]
fn _greater_member_hierarchy(
&self,
cache: impl AsRef<Cache>,
lhs_id: UserId,
rhs_id: UserId,
) -> Option<UserId> {
fn _greater_member_hierarchy(&self, lhs_id: UserId, rhs_id: UserId) -> Option<UserId> {
// Check that the IDs are the same. If they are, neither is greater.
if lhs_id == rhs_id {
return None;
Expand All @@ -1394,10 +1416,13 @@ impl Guild {
return Some(rhs_id);
}

let lhs =
self.members.get(&lhs_id)?.highest_role_info(&cache).unwrap_or((RoleId::new(1), 0));
let rhs =
self.members.get(&rhs_id)?.highest_role_info(&cache).unwrap_or((RoleId::new(1), 0));
let lhs = self
.member_highest_role(self.members.get(&lhs_id)?)
.map_or((RoleId::new(1), 0), |r| (r.id, r.position));

let rhs = self
.member_highest_role(self.members.get(&rhs_id)?)
.map_or((RoleId::new(1), 0), |r| (r.id, r.position));

// If LHS and RHS both have no top position or have the same role ID, then no one wins.
if (lhs.1 == 0 && rhs.1 == 0) || (lhs.0 == rhs.0) {
Expand Down
66 changes: 4 additions & 62 deletions src/model/guild/partial_guild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,74 +1020,16 @@ impl PartialGuild {
/// [`position`]: Role::position
#[cfg(feature = "cache")]
#[inline]
#[deprecated = "Use Cache::guild and Guild::greater_member_hierarchy"]
pub fn greater_member_hierarchy(
&self,
cache: impl AsRef<Cache>,
lhs_id: impl Into<UserId>,
rhs_id: impl Into<UserId>,
) -> Option<UserId> {
self._greater_member_hierarchy(&cache, lhs_id.into(), rhs_id.into())
}

#[cfg(feature = "cache")]
fn _greater_member_hierarchy(
&self,
cache: impl AsRef<Cache>,
lhs_id: UserId,
rhs_id: UserId,
) -> Option<UserId> {
// Check that the IDs are the same. If they are, neither is greater.
if lhs_id == rhs_id {
return None;
}

// Check if either user is the guild owner.
if lhs_id == self.owner_id {
return Some(lhs_id);
} else if rhs_id == self.owner_id {
return Some(rhs_id);
}

let (lhs, rhs) = {
let cache = cache.as_ref();
let default = (RoleId::new(1), 0);

// Clone is necessary, highest_role_info goes into cache.
let (lhs, rhs) = {
let guild = cache.guild(self.id)?;
(guild.members.get(&lhs_id)?.clone(), guild.members.get(&rhs_id)?.clone())
};

(
lhs.highest_role_info(cache).unwrap_or(default),
rhs.highest_role_info(cache).unwrap_or(default),
)
};

// If LHS and RHS both have no top position or have the same role ID, then no one wins.
if (lhs.1 == 0 && rhs.1 == 0) || (lhs.0 == rhs.0) {
return None;
}

// If LHS's top position is higher than RHS, then LHS wins.
if lhs.1 > rhs.1 {
return Some(lhs_id);
}

// If RHS's top position is higher than LHS, then RHS wins.
if rhs.1 > lhs.1 {
return Some(rhs_id);
}

// If LHS and RHS both have the same position, but LHS has the lower role ID, then LHS
// wins.
//
// If RHS has the higher role ID, then RHS wins.
if lhs.1 == rhs.1 && lhs.0 < rhs.0 {
Some(lhs_id)
} else {
Some(rhs_id)
}
let cache = cache.as_ref();
let guild = cache.guild(self.id)?;
guild.greater_member_hierarchy(cache, lhs_id, rhs_id)
}

/// Calculate a [`Member`]'s permissions in the guild.
Expand Down

0 comments on commit d29962d

Please sign in to comment.