feat: implement is_above for roles and reuse same functions from channels!
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful

This commit is contained in:
Radical 2025-05-27 11:16:33 +00:00
parent 39d01bb0d0
commit 1aa38631b8
8 changed files with 81 additions and 41 deletions

View file

@ -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;

View file

@ -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;

View file

@ -14,7 +14,6 @@ use crate::{
#[derive(Deserialize)]
struct GuildInfo {
name: String,
description: Option<String>,
}
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?;

View file

@ -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),

View file

@ -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")]

View file

@ -105,8 +105,8 @@ diesel::table! {
#[max_length = 50]
name -> Varchar,
color -> Int4,
position -> Int4,
permissions -> Int8,
is_above -> Nullable<Uuid>,
}
}

View file

@ -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<T>(
query_result: Result<Vec<T>, diesel::result::Error>,
) -> Result<Vec<T>, 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<String>,
owner_uuid: Uuid,
) -> Result<Self, Error> {
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<Uuid>,
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<Vec<Self>, Error> {
use roles::dsl;
@ -524,21 +548,36 @@ impl Role {
pub async fn new(conn: &mut Conn, guild_uuid: Uuid, name: String) -> Result<Self, Error> {
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)
}
}

View file

@ -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<String, Error> {
))
}
pub async fn order_channels(mut channels: Vec<Channel>) -> Result<Vec<Channel>, Error> {
pub async fn order_by_is_above<T>(mut items: Vec<T>) -> Result<Vec<T>, 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));
}
}