use diesel::{ ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, insert_into, delete, }; use diesel_async::RunQueryDsl; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::{ AppState, Conn, error::Error, objects::{GuildBan, Me, Permissions, Role}, schema::{guild_bans, guild_members}, }; use super::{User, load_or_empty}; #[derive(Serialize, Queryable, Selectable, Insertable)] #[diesel(table_name = guild_members)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct MemberBuilder { pub uuid: Uuid, pub nickname: Option, pub user_uuid: Uuid, pub guild_uuid: Uuid, pub is_owner: bool, } impl MemberBuilder { pub async fn build(&self, app_state: &AppState, me: Option<&Me>) -> Result { let user; if let Some(me) = me { user = User::fetch_one_with_friendship(app_state, me, self.user_uuid).await?; } else { user = User::fetch_one(app_state, self.user_uuid).await?; } Ok(Member { uuid: self.uuid, nickname: self.nickname.clone(), user_uuid: self.user_uuid, guild_uuid: self.guild_uuid, is_owner: self.is_owner, user, }) } pub async fn check_permission( &self, app_state: &AppState, permission: Permissions, ) -> Result<(), Error> { if !self.is_owner { let roles = Role::fetch_from_member(app_state, self.uuid).await?; let allowed = roles.iter().any(|r| r.permissions & permission as i64 != 0); if !allowed { return Err(Error::Forbidden("Not allowed".to_string())); } } Ok(()) } } #[derive(Serialize, Deserialize)] pub struct Member { pub uuid: Uuid, pub nickname: Option, pub user_uuid: Uuid, pub guild_uuid: Uuid, pub is_owner: bool, user: User, } impl Member { pub async fn count(conn: &mut Conn, guild_uuid: Uuid) -> Result { use guild_members::dsl; let count: i64 = dsl::guild_members .filter(dsl::guild_uuid.eq(guild_uuid)) .count() .get_result(conn) .await?; Ok(count) } pub async fn check_membership( conn: &mut Conn, user_uuid: Uuid, guild_uuid: Uuid, ) -> Result { use guild_members::dsl; let member_builder = dsl::guild_members .filter(dsl::user_uuid.eq(user_uuid)) .filter(dsl::guild_uuid.eq(guild_uuid)) .select(MemberBuilder::as_select()) .get_result(conn) .await?; Ok(member_builder) } pub async fn fetch_one( app_state: &AppState, me: &Me, user_uuid: Uuid, guild_uuid: Uuid, ) -> Result { let mut conn = app_state.pool.get().await?; use guild_members::dsl; let member: MemberBuilder = dsl::guild_members .filter(dsl::user_uuid.eq(user_uuid)) .filter(dsl::guild_uuid.eq(guild_uuid)) .select(MemberBuilder::as_select()) .get_result(&mut conn) .await?; member.build(app_state, Some(me)).await } pub async fn fetch_one_with_member( app_state: &AppState, me: Option<&Me>, uuid: Uuid, ) -> Result { let mut conn = app_state.pool.get().await?; use guild_members::dsl; let member: MemberBuilder = dsl::guild_members .filter(dsl::uuid.eq(uuid)) .select(MemberBuilder::as_select()) .get_result(&mut conn) .await?; member.build(app_state, me).await } pub async fn fetch_all( app_state: &AppState, me: &Me, guild_uuid: Uuid, ) -> Result, Error> { let mut conn = app_state.pool.get().await?; use guild_members::dsl; let member_builders: Vec = load_or_empty( dsl::guild_members .filter(dsl::guild_uuid.eq(guild_uuid)) .select(MemberBuilder::as_select()) .load(&mut conn) .await, )?; let mut members = vec![]; for builder in member_builders { members.push(builder.build(app_state, Some(me)).await?); } Ok(members) } pub async fn new( app_state: &AppState, user_uuid: Uuid, guild_uuid: Uuid, ) -> Result { let mut conn = app_state.pool.get().await?; let banned = GuildBan::fetch_one(&mut conn, guild_uuid, user_uuid).await; match banned { Ok(_) => Err(Error::Forbidden("User banned".to_string())), Err(Error::SqlError(diesel::result::Error::NotFound)) => Ok(()), Err(e) => Err(e), }?; let member_uuid = Uuid::now_v7(); let member = MemberBuilder { uuid: member_uuid, guild_uuid, user_uuid, nickname: None, is_owner: false, }; insert_into(guild_members::table) .values(&member) .execute(&mut conn) .await?; member.build(app_state, None).await } pub async fn delete(self, conn: &mut Conn) -> Result<(), Error> { delete(guild_members::table) .filter(guild_members::uuid.eq(self.uuid)) .execute(conn) .await?; Ok(()) } pub async fn ban(self, conn: &mut Conn, reason: &String) -> Result<(), Error> { if self.is_owner { return Err(Error::Forbidden("Can not ban owner".to_string())); } use guild_bans::dsl; insert_into(guild_bans::table) .values(( dsl::guild_uuid.eq(self.guild_uuid), dsl::user_uuid.eq(self.user_uuid), dsl::reason.eq(reason), )) .execute(conn) .await?; self.delete(conn).await?; Ok(()) } }