use crate::domain::dto::server::{ CreateServerRequest, ServerExplorerItemResponse, ServerResponse, ServerRolePermissionResponse, ServerTreeResponse, ServerUserPermissionResponse, UpdateServerRequest, }; use crate::models::{server, server_item_order, server_role_permission, server_user_permission}; use crate::repositories::types::{CategoryWithPermissions, ChannelWithPermissions}; use crate::routes::category::mapper::category_model_to_category_response_with_permission; use crate::routes::channel::mapper::channel_model_to_channel_response_with_permission; use sea_orm::Set; use std::collections::HashMap; use uuid::Uuid; pub fn server_model_to_server_response(model: server::Model) -> ServerResponse { ServerResponse { id: model.id, name: model.name, is_default: model.is_default, created_at: model.created_at, updated_at: model.updated_at, } } pub fn create_request_to_am(req: CreateServerRequest) -> server::ActiveModel { server::ActiveModel { id: Set(Uuid::new_v4()), name: Set(req.name), password: Set(req.password), is_default: Set(req.is_default), ..Default::default() } } pub fn update_request_to_am(id: Uuid, req: UpdateServerRequest) -> server::ActiveModel { server::ActiveModel { id: Set(id), name: Set(req.name), password: Set(req.password), is_default: Set(req.is_default), ..Default::default() } } pub fn server_user_permission_to_response( model: server_user_permission::Model, ) -> ServerUserPermissionResponse { ServerUserPermissionResponse { id: model.id, server_id: model.server_id, user_id: model.user_id, permissions: model.permissions as u64, } } pub fn server_role_permission_to_response( model: server_role_permission::Model, ) -> ServerRolePermissionResponse { ServerRolePermissionResponse { id: model.id, server_id: model.server_id, role_id: model.role_id, permissions: model.permissions as u64, } } pub fn build_server_tree( orders: Vec, channels: Vec, categories: Vec, ) -> ServerTreeResponse { let order_map: HashMap<(Option, Uuid), i64> = orders .into_iter() .map(|order| { ( (order.parent_category_id, order.resource_id), order.order_key, ) }) .collect(); let mut category_channels: HashMap> = HashMap::new(); let mut orphan_channels: Vec = Vec::new(); for channel_with_permissions in channels { if let Some(category_id) = channel_with_permissions.channel.category_id { category_channels .entry(category_id) .or_default() .push(channel_with_permissions); } else { orphan_channels.push(channel_with_permissions); } } // Trier les canaux dans chaque catégorie par leur order_key for chans in category_channels.values_mut() { chans.sort_by_key(|c| { order_map .get(&(c.channel.category_id, c.channel.id)) .copied() .unwrap_or(i64::MAX) }); } // Préparer les éléments racine (catégories & canaux orphelins) avec leur order_key let mut root_items: Vec<(ServerExplorerItemResponse, i64)> = Vec::new(); for cat_with_perm in categories { let cat_id = cat_with_perm.category.id; let order_key = order_map.get(&(None, cat_id)).copied().unwrap_or(i64::MAX); let chans = category_channels.remove(&cat_id).unwrap_or_default(); let cat_perm_bits = cat_with_perm.permissions.map(|p| p.bits()).unwrap_or(0); let cat_response = category_model_to_category_response_with_permission( cat_with_perm.category, Some(cat_perm_bits), ); let chan_responses = chans .into_iter() .map(|c| { let chan_perm_bits = c.permissions.map(|p| p.bits()).unwrap_or(0); channel_model_to_channel_response_with_permission(c.channel, Some(chan_perm_bits)) }) .collect(); root_items.push(( ServerExplorerItemResponse::Category(cat_response, chan_responses), order_key, )); } for chan_with_perm in orphan_channels { let order_key = order_map .get(&(None, chan_with_perm.channel.id)) .copied() .unwrap_or(i64::MAX); let chan_perm_bits = chan_with_perm.permissions.map(|p| p.bits()).unwrap_or(0); let chan_response = channel_model_to_channel_response_with_permission( chan_with_perm.channel, Some(chan_perm_bits), ); root_items.push(( ServerExplorerItemResponse::Channel(chan_response), order_key, )); } // Tri du niveau racine par order_key root_items.sort_by_key(|(_, order_key)| *order_key); let items = root_items.into_iter().map(|(item, _)| item).collect(); ServerTreeResponse { items } } #[cfg(test)] mod tests { use super::*; use crate::models::{category, channel}; use crate::permissions::ChannelPermission; use chrono::Utc; fn channel(id: Uuid, category_id: Option, name: &str) -> ChannelWithPermissions { ChannelWithPermissions { channel: channel::Model { id, server_id: Some(Uuid::new_v4()), category_id, channel_type: channel::ChannelType::Text, name: Some(name.to_owned()), created_at: Utc::now(), updated_at: Utc::now(), }, permissions: Some(ChannelPermission::empty()), } } #[test] fn sorts_channels_by_order_key_within_each_category() { let category_id = Uuid::new_v4(); let first_id = Uuid::new_v4(); let second_id = Uuid::new_v4(); let category = CategoryWithPermissions { category: category::Model { id: category_id, server_id: Uuid::new_v4(), name: "Discussion".to_owned(), created_at: Utc::now(), updated_at: Utc::now(), }, permissions: Some(ChannelPermission::empty()), }; let orders = vec![ server_item_order::Model { id: Uuid::new_v4(), server_id: category.category.server_id, resource_id: category_id, resource_type: server_item_order::OrderedResourceType::Category, parent_category_id: None, order_key: 1, created_at: Utc::now(), updated_at: Utc::now(), }, server_item_order::Model { id: Uuid::new_v4(), server_id: category.category.server_id, resource_id: first_id, resource_type: server_item_order::OrderedResourceType::Channel, parent_category_id: Some(category_id), order_key: 2, created_at: Utc::now(), updated_at: Utc::now(), }, server_item_order::Model { id: Uuid::new_v4(), server_id: category.category.server_id, resource_id: second_id, resource_type: server_item_order::OrderedResourceType::Channel, parent_category_id: Some(category_id), order_key: 1, created_at: Utc::now(), updated_at: Utc::now(), }, ]; let response = build_server_tree( orders, vec![ channel(first_id, Some(category_id), "second"), channel(second_id, Some(category_id), "first"), ], vec![category], ); let ServerExplorerItemResponse::Category(_, channels) = &response.items[0] else { panic!("expected a category"); }; assert_eq!(channels[0].name.as_deref(), Some("first")); assert_eq!(channels[1].name.as_deref(), Some("second")); } }