From 1aa38631b8b0f125599ef83d90eba5d66d28d2d0 Mon Sep 17 00:00:00 2001 From: Radical Date: Tue, 27 May 2025 11:16:33 +0000 Subject: [PATCH] feat: implement is_above for roles and reuse same functions from channels! --- .../down.sql | 3 + .../up.sql | 3 + src/api/v1/servers/mod.rs | 2 - src/api/v1/servers/uuid/channels/mod.rs | 8 +- src/api/v1/servers/uuid/roles/mod.rs | 12 ++- src/schema.rs | 2 +- src/structs.rs | 73 ++++++++++++++----- src/utils.rs | 19 +++-- 8 files changed, 81 insertions(+), 41 deletions(-) create mode 100644 migrations/2025-05-27-105059_redo_role_ordering/down.sql create mode 100644 migrations/2025-05-27-105059_redo_role_ordering/up.sql diff --git a/migrations/2025-05-27-105059_redo_role_ordering/down.sql b/migrations/2025-05-27-105059_redo_role_ordering/down.sql new file mode 100644 index 0000000..6b38e1e --- /dev/null +++ b/migrations/2025-05-27-105059_redo_role_ordering/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE roles ADD COLUMN position int NOT NULL DEFAULT 0; +ALTER TABLE roles DROP COLUMN is_above; diff --git a/migrations/2025-05-27-105059_redo_role_ordering/up.sql b/migrations/2025-05-27-105059_redo_role_ordering/up.sql new file mode 100644 index 0000000..d426ab7 --- /dev/null +++ b/migrations/2025-05-27-105059_redo_role_ordering/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE roles DROP COLUMN position; +ALTER TABLE roles ADD COLUMN is_above UUID UNIQUE REFERENCES roles(uuid) DEFAULT NULL; diff --git a/src/api/v1/servers/mod.rs b/src/api/v1/servers/mod.rs index 91980e3..987d439 100644 --- a/src/api/v1/servers/mod.rs +++ b/src/api/v1/servers/mod.rs @@ -14,7 +14,6 @@ use crate::{ #[derive(Deserialize)] struct GuildInfo { name: String, - description: Option, } pub fn web() -> Scope { @@ -41,7 +40,6 @@ pub async fn create( let guild = Guild::new( &mut conn, guild_info.name.clone(), - guild_info.description.clone(), uuid, ) .await?; diff --git a/src/api/v1/servers/uuid/channels/mod.rs b/src/api/v1/servers/uuid/channels/mod.rs index c3640fe..d87aba2 100644 --- a/src/api/v1/servers/uuid/channels/mod.rs +++ b/src/api/v1/servers/uuid/channels/mod.rs @@ -1,9 +1,5 @@ use crate::{ - Data, - api::v1::auth::check_access_token, - error::Error, - structs::{Channel, Member}, - utils::{get_auth_header, order_channels}, + api::v1::auth::check_access_token, error::Error, structs::{Channel, Member}, utils::{get_auth_header, order_by_is_above}, Data }; use ::uuid::Uuid; use actix_web::{HttpRequest, HttpResponse, get, post, web}; @@ -43,7 +39,7 @@ pub async fn get( let channels = Channel::fetch_all(&data.pool, guild_uuid).await?; - let channels_ordered = order_channels(channels).await?; + let channels_ordered = order_by_is_above(channels).await?; data.set_cache_key( format!("{}_channels", guild_uuid), diff --git a/src/api/v1/servers/uuid/roles/mod.rs b/src/api/v1/servers/uuid/roles/mod.rs index db6c7cf..c0ebaa2 100644 --- a/src/api/v1/servers/uuid/roles/mod.rs +++ b/src/api/v1/servers/uuid/roles/mod.rs @@ -3,11 +3,7 @@ use actix_web::{HttpRequest, HttpResponse, get, post, web}; use serde::Deserialize; use crate::{ - Data, - api::v1::auth::check_access_token, - error::Error, - structs::{Member, Role}, - utils::get_auth_header, + api::v1::auth::check_access_token, error::Error, structs::{Member, Role}, utils::{get_auth_header, order_by_is_above}, Data }; pub mod uuid; @@ -43,10 +39,12 @@ pub async fn get( let roles = Role::fetch_all(&mut conn, guild_uuid).await?; - data.set_cache_key(format!("{}_roles", guild_uuid), roles.clone(), 1800) + let roles_ordered = order_by_is_above(roles).await?; + + data.set_cache_key(format!("{}_roles", guild_uuid), roles_ordered.clone(), 1800) .await?; - Ok(HttpResponse::Ok().json(roles)) + Ok(HttpResponse::Ok().json(roles_ordered)) } #[post("{uuid}/roles")] diff --git a/src/schema.rs b/src/schema.rs index 8a85a2e..cc5e97c 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -105,8 +105,8 @@ diesel::table! { #[max_length = 50] name -> Varchar, color -> Int4, - position -> Int4, permissions -> Int8, + is_above -> Nullable, } } diff --git a/src/structs.rs b/src/structs.rs index fc1d9ca..be4bd43 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -5,7 +5,6 @@ use diesel::{ update, }; use diesel_async::{RunQueryDsl, pooled_connection::AsyncDieselConnectionManager}; -use log::debug; use serde::{Deserialize, Serialize}; use tokio::task; use url::Url; @@ -15,9 +14,17 @@ use crate::{ Conn, Data, error::Error, schema::*, - utils::{image_check, order_channels}, + utils::{image_check, order_by_is_above}, }; +pub trait HasUuid { + fn uuid(&self) -> &Uuid; +} + +pub trait HasIsAbove { + fn is_above(&self) -> Option<&Uuid>; +} + fn load_or_empty( query_result: Result, diesel::result::Error>, ) -> Result, diesel::result::Error> { @@ -79,6 +86,18 @@ pub struct ChannelPermission { pub permissions: i64, } +impl HasUuid for Channel { + fn uuid(&self) -> &Uuid { + self.uuid.as_ref() + } +} + +impl HasIsAbove for Channel { + fn is_above(&self) -> Option<&Uuid> { + self.is_above.as_ref() + } +} + impl Channel { pub async fn fetch_all( pool: &deadpool::managed::Pool< @@ -129,11 +148,7 @@ impl Channel { let channels = Self::fetch_all(&data.pool, guild_uuid).await?; - debug!("{:?}", channels); - - let channels_ordered = order_channels(channels).await?; - - debug!("{:?}", channels_ordered); + let channels_ordered = order_by_is_above(channels).await?; let last_channel = channels_ordered.last(); @@ -145,8 +160,6 @@ impl Channel { is_above: None, }; - debug!("New Channel: {:?}", new_channel); - insert_into(channels::table) .values(new_channel.clone()) .execute(&mut conn) @@ -359,7 +372,6 @@ impl Guild { pub async fn new( conn: &mut Conn, name: String, - description: Option, owner_uuid: Uuid, ) -> Result { let guild_uuid = Uuid::now_v7(); @@ -367,7 +379,7 @@ impl Guild { let guild_builder = GuildBuilder { uuid: guild_uuid, name: name.clone(), - description: description.clone(), + description: None, icon: None, owner_uuid, }; @@ -394,7 +406,7 @@ impl Guild { Ok(Guild { uuid: guild_uuid, name, - description, + description: None, icon: None, owner_uuid, roles: vec![], @@ -492,10 +504,22 @@ pub struct Role { guild_uuid: Uuid, name: String, color: i32, - position: i32, + is_above: Option, permissions: i64, } +impl HasUuid for Role { + fn uuid(&self) -> &Uuid { + self.uuid.as_ref() + } +} + +impl HasIsAbove for Role { + fn is_above(&self) -> Option<&Uuid> { + self.is_above.as_ref() + } +} + impl Role { pub async fn fetch_all(conn: &mut Conn, guild_uuid: Uuid) -> Result, Error> { use roles::dsl; @@ -524,21 +548,36 @@ impl Role { pub async fn new(conn: &mut Conn, guild_uuid: Uuid, name: String) -> Result { let role_uuid = Uuid::now_v7(); - let role = Role { + let roles = Self::fetch_all(conn, guild_uuid).await?; + + let roles_ordered = order_by_is_above(roles).await?; + + let last_role = roles_ordered.last(); + + let new_role = Role { uuid: role_uuid, guild_uuid, name, color: 16777215, - position: 0, + is_above: None, permissions: 0, }; insert_into(roles::table) - .values(role.clone()) + .values(new_role.clone()) .execute(conn) .await?; - Ok(role) + if let Some(old_last_role) = last_role { + use roles::dsl; + update(roles::table) + .filter(dsl::uuid.eq(old_last_role.uuid)) + .set(dsl::is_above.eq(new_role.uuid)) + .execute(conn) + .await?; + } + + Ok(new_role) } } diff --git a/src/utils.rs b/src/utils.rs index e68f38a..3edaab8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,7 +9,7 @@ use hex::encode; use redis::RedisError; use serde::Serialize; -use crate::{Data, error::Error, structs::Channel}; +use crate::{error::Error, structs::{HasIsAbove, HasUuid}, Data}; pub fn get_auth_header(headers: &HeaderMap) -> Result<&str, Error> { let auth_token = headers.get(actix_web::http::header::AUTHORIZATION); @@ -119,22 +119,25 @@ pub fn image_check(icon: BytesMut) -> Result { )) } -pub async fn order_channels(mut channels: Vec) -> Result, Error> { +pub async fn order_by_is_above(mut items: Vec) -> Result, Error> +where + T: HasUuid + HasIsAbove, +{ let mut ordered = Vec::new(); // Find head - let head_pos = channels + let head_pos = items .iter() - .position(|channel| !channels.iter().any(|i| i.is_above == Some(channel.uuid))); + .position(|item| !items.iter().any(|i| i.is_above() == Some(item.uuid()))); if let Some(pos) = head_pos { - ordered.push(channels.swap_remove(pos)); + ordered.push(items.swap_remove(pos)); - while let Some(next_pos) = channels + while let Some(next_pos) = items .iter() - .position(|channel| Some(channel.uuid) == ordered.last().unwrap().is_above) + .position(|item| Some(item.uuid()) == ordered.last().unwrap().is_above()) { - ordered.push(channels.swap_remove(next_pos)); + ordered.push(items.swap_remove(next_pos)); } }