Revue de code et refactoring
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user