Revue de code et refactoring

This commit is contained in:
2025-10-06 20:24:04 +02:00
parent adeb37ce83
commit adb6555aa6
20 changed files with 445 additions and 440 deletions

View File

@@ -21,7 +21,3 @@ axum-embed = "0.1.0"
rust-embed = "8.7.2"
utoipa = { version = "5.4.0", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
[dependencies.pmoupnp]
path = "../pmoupnp"
default-features = false

View File

@@ -20,7 +20,6 @@
//!
//! - [`server`] : Implémentation du serveur principal et du builder
//! - [`logs`] : Système de logs SSE pour monitoring en temps réel
//! - `upnp_impl` : Implémentation du trait `pmoupnp::UpnpServer` (privé)
//!
//! ## Exemple d'utilisation
//!
@@ -53,27 +52,25 @@
//!
//! ## Intégration UPnP
//!
//! Le serveur implémente automatiquement le trait `pmoupnp::UpnpServer`, permettant
//! de connecter des devices UPnP :
//! Le serveur peut être étendu avec UPnP via le trait `pmoupnp::UpnpServer`.
//! L'implémentation est fournie par `pmoupnp` (feature `pmoserver`), permettant
//! de connecter des devices UPnP sans que `pmoserver` dépende de `pmoupnp` :
//!
//! ```rust,no_run
//! use pmoupnp::{UpnpServer, mediarenderer::device::MEDIA_RENDERER};
//! use pmoupnp::devices::DeviceInstance;
//! use pmoupnp::{UpnpServer, mediarenderer::MEDIA_RENDERER};
//! use pmoserver::ServerBuilder;
//! use std::sync::Arc;
//!
//! # async fn example() {
//! let mut server = ServerBuilder::new("MediaRenderer").build();
//! let device = Arc::new(DeviceInstance::new(&MEDIA_RENDERER));
//! let device = MEDIA_RENDERER.create_instance();
//!
//! // Le device enregistre automatiquement ses routes
//! // Le trait UpnpServer est automatiquement disponible (implémenté dans pmoupnp)
//! device.register_urls(&mut server).await;
//! # }
//! ```
pub mod server;
pub mod logs;
mod upnp_impl;
pub use server::{Server, ServerBuilder, ServerInfo};
pub use logs::{LogState, SseLayer, log_sse, log_dump};
pub use logs::{LogState, SseLayer, log_sse, log_dump, init_logging, LoggingOptions};

View File

@@ -19,6 +19,7 @@ use axum::{
};
use serde::{Deserialize, Serialize};
use tokio::sync::broadcast;
use tracing_subscriber::{Registry, layer::SubscriberExt};
/// Représente une entrée de log
#[derive(Debug, Clone, Serialize)]
@@ -157,3 +158,60 @@ fn filter_entry(entry: &LogEntry, q: &LogQuery) -> bool {
allowed
}
/// Options d'initialisation du système de logging
#[derive(Debug, Clone)]
pub struct LoggingOptions {
/// Capacité du buffer circulaire (nombre d'entrées conservées)
pub buffer_capacity: usize,
/// Activer la sortie vers stderr/stdout
pub enable_console: bool,
}
impl Default for LoggingOptions {
fn default() -> Self {
Self {
buffer_capacity: 1000,
enable_console: true,
}
}
}
/// Initialise le système de logging avec SSE et optionnellement la console
///
/// # Arguments
/// * `options` - Options de configuration du logging
///
/// # Retourne
/// Le `LogState` qui peut être utilisé pour ajouter les routes de logging au serveur
///
/// # Exemple
/// ```rust,no_run
/// use pmoserver::logs::{init_logging, LoggingOptions};
///
/// let log_state = init_logging(LoggingOptions {
/// buffer_capacity: 1000,
/// enable_console: true,
/// });
/// ```
pub fn init_logging(options: LoggingOptions) -> LogState {
let log_state = LogState::new(options.buffer_capacity);
let subscriber = Registry::default().with(SseLayer::new(log_state.clone()));
if options.enable_console {
let subscriber = subscriber.with(
tracing_subscriber::fmt::layer()
.with_target(true)
.with_level(true)
.with_ansi(true),
);
tracing::subscriber::set_global_default(subscriber)
.expect("Failed to set global default subscriber");
} else {
tracing::subscriber::set_global_default(subscriber)
.expect("Failed to set global default subscriber");
}
log_state
}

View File

@@ -25,6 +25,7 @@ use std::{net::SocketAddr, sync::Arc};
use tokio::{signal, sync::RwLock, task::JoinHandle};
use tracing::info;
use utoipa_swagger_ui::SwaggerUi;
use crate::logs::{LogState, LoggingOptions, init_logging, log_sse, log_dump};
/// Info serveur sérialisable
#[derive(Clone, Serialize, utoipa::ToSchema)]
@@ -45,6 +46,7 @@ pub struct Server {
router: Arc<RwLock<Router>>,
api_router: Arc<RwLock<Option<Router>>>,
join_handle: Option<JoinHandle<()>>,
log_state: Option<LogState>,
}
impl Server {
@@ -70,6 +72,7 @@ impl Server {
router: Arc::new(RwLock::new(Router::new())),
api_router: Arc::new(RwLock::new(None)),
join_handle: None,
log_state: None,
}
}
@@ -508,6 +511,45 @@ impl Server {
http_port: self.http_port,
}
}
/// Initialise le système de logging et enregistre les routes de logs
///
/// Cette méthode configure le système de tracing avec SSE et optionnellement la console,
/// puis enregistre automatiquement les routes `/log-sse` et `/log-dump`.
///
/// # Arguments
///
/// * `options` - Options de configuration du logging
///
/// # Exemple
///
/// ```rust,no_run
/// # use pmoserver::{ServerBuilder, logs::LoggingOptions};
/// # #[tokio::main]
/// # async fn main() {
/// let mut server = ServerBuilder::new_configured().build();
///
/// // Initialiser les logs avec console
/// server.init_logging(LoggingOptions::default()).await;
///
/// // Ou sans console
/// server.init_logging(LoggingOptions {
/// buffer_capacity: 1000,
/// enable_console: false,
/// }).await;
///
/// server.start().await;
/// # }
/// ```
pub async fn init_logging(&mut self, options: LoggingOptions) {
let log_state = init_logging(options);
// Enregistrer automatiquement les routes de logging
self.add_handler_with_state("/log-sse", log_sse, log_state.clone()).await;
self.add_handler_with_state("/log-dump", log_dump, log_state.clone()).await;
self.log_state = Some(log_state);
}
}
/// Builder pattern

View File

@@ -1,94 +0,0 @@
//! Implémentation du trait UpnpServer pour le serveur pmoserver
//!
//! Ce module fournit l'implémentation du trait [`pmoupnp::UpnpServer`] pour
//! le [`Server`](crate::server::Server) de pmoserver, permettant aux devices
//! et services UPnP d'enregistrer automatiquement leurs endpoints HTTP.
//!
//! ## Architecture
//!
//! L'implémentation fait le pont entre :
//! - Les pointeurs de fonction du trait `UpnpServer` (agnostiques du framework web)
//! - Les handlers Axum (spécifiques à l'implémentation `pmoserver`)
//!
//! Chaque méthode du trait crée un wrapper qui :
//! 1. Convertit les pointeurs de fonction en closures compatibles Axum
//! 2. Délègue l'enregistrement aux méthodes internes du `Server`
//! 3. Retourne une future qui se résout une fois le handler enregistré
//!
//! ## Exemple d'utilisation
//!
//! ```rust,no_run
//! use pmoupnp::{UpnpServer, mediarenderer::device::MEDIA_RENDERER};
//! use pmoupnp::devices::DeviceInstance;
//! use pmoserver::ServerBuilder;
//! use std::sync::Arc;
//!
//! # async fn example() {
//! let mut server = ServerBuilder::new("MyRenderer").build();
//! let device = Arc::new(DeviceInstance::new(&MEDIA_RENDERER));
//!
//! // Le trait UpnpServer est automatiquement disponible
//! device.register_urls(&mut server).await;
//! # }
//! ```
use crate::server::Server;
use pmoupnp::{UpnpServer, server::{Response, HeaderMap, Request}};
use std::future::Future;
use std::pin::Pin;
use axum::extract::State;
impl UpnpServer for Server {
fn add_handler<F, Fut>(&mut self, path: &str, handler: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
F: Fn() -> Fut + Send + Sync + 'static + Clone,
Fut: Future<Output = Response> + Send + 'static,
{
let path = path.to_string();
Box::pin(async move {
Self::add_handler(self, &path, handler).await;
})
}
fn add_post_handler_with_state<S>(
&mut self,
path: &str,
handler: fn(State<S>, String) -> Pin<Box<dyn Future<Output = Response> + Send>>,
state: S,
) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
S: Clone + Send + Sync + 'static,
{
let path = path.to_string();
// Créer un wrapper qui convertit le fn pointer en handler Axum
let wrapper = move |State(s): State<S>, body: String| -> Pin<Box<dyn Future<Output = Response> + Send>> {
handler(State(s), body)
};
Box::pin(async move {
Self::add_post_handler_with_state(self, &path, wrapper, state).await;
})
}
fn add_handler_with_state<S>(
&mut self,
path: &str,
handler: fn(State<S>, HeaderMap, Request) -> Pin<Box<dyn Future<Output = Response> + Send>>,
state: S,
) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
S: Clone + Send + Sync + 'static,
{
let path = path.to_string();
// Créer un wrapper qui convertit le fn pointer en handler Axum
let wrapper = move |State(s): State<S>, headers: HeaderMap, req: Request| -> Pin<Box<dyn Future<Output = Response> + Send>> {
handler(State(s), headers, req)
};
Box::pin(async move {
Self::add_handler_with_state(self, &path, wrapper, state).await;
})
}
}