Files
oxspeak_server/src/routes/server/mapper.rs
T
2026-08-02 10:13:49 +02:00

244 lines
8.2 KiB
Rust

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<server_item_order::Model>,
channels: Vec<ChannelWithPermissions>,
categories: Vec<CategoryWithPermissions>,
) -> ServerTreeResponse {
let order_map: HashMap<(Option<Uuid>, Uuid), i64> = orders
.into_iter()
.map(|order| {
(
(order.parent_category_id, order.resource_id),
order.order_key,
)
})
.collect();
let mut category_channels: HashMap<Uuid, Vec<ChannelWithPermissions>> = HashMap::new();
let mut orphan_channels: Vec<ChannelWithPermissions> = 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<Uuid>, 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"));
}
}