2025-10-09 20:37:05 +02:00
|
|
|
//! Types et utilitaires pour les handlers d'actions UPnP.
|
|
|
|
|
//!
|
|
|
|
|
//! Ce module définit les types fondamentaux pour gérer l'exécution
|
|
|
|
|
//! asynchrone des actions UPnP.
|
|
|
|
|
//!
|
|
|
|
|
//! # Architecture
|
|
|
|
|
//!
|
|
|
|
|
//! Les actions UPnP sont exécutées de manière asynchrone via des handlers
|
|
|
|
|
//! qui prennent des données en entrée et retournent des données en sortie.
|
|
|
|
|
//!
|
|
|
|
|
//! ```text
|
|
|
|
|
//! ActionData (input)
|
|
|
|
|
//! ↓
|
|
|
|
|
//! ActionHandler (async processing)
|
|
|
|
|
//! ↓
|
|
|
|
|
//! ActionData (output)
|
|
|
|
|
//! ```
|
|
|
|
|
//!
|
|
|
|
|
//! # Examples
|
|
|
|
|
//!
|
|
|
|
|
//! ```rust
|
|
|
|
|
//! use pmoupnp::action_handler;
|
2025-10-09 22:36:33 +02:00
|
|
|
//! use pmoupnp::actions::ActionError;
|
2025-10-09 20:37:05 +02:00
|
|
|
//! use std::collections::HashMap;
|
|
|
|
|
//! use std::sync::Arc;
|
|
|
|
|
//!
|
|
|
|
|
//! // Créer un handler avec la macro
|
2025-10-19 10:06:04 +02:00
|
|
|
//! let handler = action_handler!(|mut data| {
|
2025-10-09 20:37:05 +02:00
|
|
|
//! // Traiter les données
|
2025-10-19 10:06:04 +02:00
|
|
|
//! Ok::<_, ActionError>(data)
|
2025-10-09 20:37:05 +02:00
|
|
|
//! });
|
|
|
|
|
//!
|
|
|
|
|
//! // Ou manuellement
|
2025-10-19 10:06:04 +02:00
|
|
|
//! use pmoupnp::actions::{ActionData, ActionHandler};
|
|
|
|
|
//! let manual_handler: ActionHandler = Arc::new(|data| {
|
|
|
|
|
//! Box::pin(async move { Ok::<_, ActionError>(data) })
|
2025-10-09 20:37:05 +02:00
|
|
|
//! });
|
|
|
|
|
//! ```
|
|
|
|
|
|
|
|
|
|
use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc};
|
|
|
|
|
|
2025-10-19 07:58:37 +02:00
|
|
|
use bevy_reflect::Reflect;
|
2025-10-09 20:37:05 +02:00
|
|
|
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Données d'une action UPnP (entrée/sortie unifiées).
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// Représente un ensemble de paramètres clé-valeur pour une action UPnP,
|
2025-10-19 07:58:37 +02:00
|
|
|
/// utilisant des valeurs Reflect pour la flexibilité de typage.
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// # Structure
|
|
|
|
|
///
|
|
|
|
|
/// - **Clé** : Nom du paramètre (ex: "InstanceID", "TransportURI")
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - **Valeur** : Valeur dynamique via `Box<dyn Reflect>`
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// # Exemples
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// use pmoupnp::actions::ActionData;
|
|
|
|
|
/// use std::collections::HashMap;
|
2025-10-19 07:58:37 +02:00
|
|
|
/// use bevy_reflect::Reflect;
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let mut data: ActionData = HashMap::new();
|
|
|
|
|
/// data.insert("InstanceID".to_string(), Box::new(0u32));
|
|
|
|
|
/// data.insert("Speed".to_string(), Box::new("1".to_string()));
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// // Les valeurs peuvent être modifiées
|
|
|
|
|
/// data.insert("InstanceID".to_string(), Box::new(1u32));
|
2025-10-09 20:37:05 +02:00
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Notes
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - Utilise `Box<dyn Reflect>` pour la flexibilité de typage
|
|
|
|
|
/// - Même type pour les entrées et sorties du handler
|
|
|
|
|
/// - Le handler peut modifier directement les données
|
|
|
|
|
pub type ActionData = HashMap<String, Box<dyn Reflect>>;
|
2025-10-09 20:37:05 +02:00
|
|
|
|
|
|
|
|
/// Future retourné par un [`ActionHandler`].
|
|
|
|
|
///
|
|
|
|
|
/// Ce type représente le résultat asynchrone d'un handler d'action.
|
|
|
|
|
/// Il est boxé et pinné pour permettre le polymorphisme et la manipulation
|
|
|
|
|
/// sûre des futures.
|
|
|
|
|
///
|
|
|
|
|
/// # Type complet
|
|
|
|
|
///
|
|
|
|
|
/// ```ignore
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Pin<Box<dyn Future<Output = Result<ActionData, ActionError>> + Send>>
|
2025-10-09 20:37:05 +02:00
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Composants
|
|
|
|
|
///
|
|
|
|
|
/// - `Pin<Box<...>>` : Permet de déplacer le future en mémoire sans invalidation
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - `dyn Future<Output = Result<ActionData, ActionError>>` : Future retournant un Result avec les données modifiées
|
2025-10-09 20:37:05 +02:00
|
|
|
/// - `+ Send` : Le future peut être envoyé entre threads
|
|
|
|
|
///
|
|
|
|
|
/// # Notes
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - Les handlers retournent `Ok(ActionData)` en cas de succès ou `Err(ActionError)` en cas d'erreur
|
|
|
|
|
/// - Le handler retourne les données modifiées (ActionData unifié pour entrée/sortie)
|
2025-10-09 20:37:05 +02:00
|
|
|
/// - Rarement utilisé directement (la macro `action_handler!` s'en charge)
|
|
|
|
|
/// - Nécessaire pour la compatibilité avec les trait objects
|
2025-10-19 13:42:29 +02:00
|
|
|
pub type ActionFuture =
|
|
|
|
|
Pin<Box<dyn Future<Output = Result<ActionData, crate::actions::ActionError>> + Send>>;
|
2025-10-09 20:37:05 +02:00
|
|
|
|
|
|
|
|
/// Handler d'action UPnP asynchrone.
|
|
|
|
|
///
|
|
|
|
|
/// Un `ActionHandler` est une fonction asynchrone partageable qui exécute
|
2025-10-19 07:58:37 +02:00
|
|
|
/// la logique métier d'une action et retourne les données modifiées.
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// # Signature
|
|
|
|
|
///
|
|
|
|
|
/// ```ignore
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Fn(ActionData) -> ActionFuture
|
2025-10-09 20:37:05 +02:00
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// Prend :
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - [`ActionData`] : HashMap contenant les valeurs des arguments (Box<dyn Reflect>)
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Retourne un [`ActionFuture`] qui se résout en `Result<ActionData, ActionError>`.
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// # Responsabilités
|
|
|
|
|
///
|
|
|
|
|
/// Le handler est responsable de :
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - Lire les arguments d'entrée depuis ActionData
|
2025-10-09 20:37:05 +02:00
|
|
|
/// - Exécuter la logique métier
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - Modifier les données selon les besoins
|
|
|
|
|
/// - Retourner `Ok(ActionData)` avec les données modifiées ou `Err(ActionError)` en cas d'erreur
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// La méthode [`ActionInstance::run()`](crate::actions::ActionInstance::run) s'occupe
|
2025-10-16 21:06:29 +02:00
|
|
|
/// automatiquement de :
|
2025-10-19 07:58:37 +02:00
|
|
|
/// 1. Construire ActionData depuis les StateVarInstance
|
|
|
|
|
/// 2. Merger les valeurs IN du SOAP
|
|
|
|
|
/// 3. (Si stateful) Sauver les IN dans les StateVarInstance avant le handler
|
|
|
|
|
/// 4. Exécuter le handler
|
|
|
|
|
/// 5. (Si stateful) Sauver les OUT dans les StateVarInstance après le handler
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// # Traits requis
|
|
|
|
|
///
|
|
|
|
|
/// - `Send` : Le handler peut être envoyé entre threads
|
|
|
|
|
/// - `Sync` : Le handler peut être partagé entre threads
|
|
|
|
|
/// - `Arc` : Permet le partage sans copie
|
|
|
|
|
///
|
|
|
|
|
/// # Création
|
|
|
|
|
///
|
|
|
|
|
/// ## Avec la macro (recommandé)
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// use pmoupnp::action_handler;
|
2025-10-09 22:36:33 +02:00
|
|
|
/// use pmoupnp::actions::ActionError;
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let handler = action_handler!(|data| {
|
|
|
|
|
/// // Logique métier avec ActionData
|
|
|
|
|
/// Ok(data)
|
2025-10-09 20:37:05 +02:00
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// ## Manuellement
|
|
|
|
|
///
|
2025-10-09 22:36:33 +02:00
|
|
|
/// ```rust
|
2025-10-19 07:58:37 +02:00
|
|
|
/// use pmoupnp::actions::{ActionHandler, ActionData, ActionError};
|
2025-10-09 20:37:05 +02:00
|
|
|
/// use std::sync::Arc;
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let handler: ActionHandler = Arc::new(|data| {
|
2025-10-09 20:37:05 +02:00
|
|
|
/// Box::pin(async move {
|
|
|
|
|
/// // Votre logique async
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Ok(data)
|
2025-10-09 20:37:05 +02:00
|
|
|
/// })
|
|
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Notes d'implémentation
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - Le handler reçoit et retourne ActionData (type unifié entrée/sortie)
|
|
|
|
|
/// - Le handler peut modifier directement les données reçues
|
2025-10-09 20:37:05 +02:00
|
|
|
/// - Le handler capture les variables par `move`
|
|
|
|
|
/// - Le future est automatiquement `Send` si les captures le sont
|
|
|
|
|
/// - Utilisez la macro `action_handler!` pour simplifier la création
|
2025-10-19 07:58:37 +02:00
|
|
|
pub type ActionHandler = Arc<dyn Fn(ActionData) -> ActionFuture + Send + Sync>;
|
2025-10-09 20:37:05 +02:00
|
|
|
|
|
|
|
|
/// Macro pour créer facilement un ActionHandler.
|
|
|
|
|
///
|
|
|
|
|
/// Cette macro simplifie la création d'handlers asynchrones en cachant
|
|
|
|
|
/// la complexité de `Arc`, `Box::pin`, et `async move`.
|
|
|
|
|
///
|
|
|
|
|
/// # Syntaxe
|
|
|
|
|
///
|
|
|
|
|
/// ```ignore
|
2025-10-19 07:58:37 +02:00
|
|
|
/// action_handler!(|data| {
|
|
|
|
|
/// // votre logique async avec ActionData
|
|
|
|
|
/// // Modifier les données et les retourner
|
|
|
|
|
/// Ok(data)
|
2025-10-09 20:37:05 +02:00
|
|
|
/// })
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - `data` : Paramètre de type [`ActionData`] - HashMap contenant les valeurs des arguments
|
2025-10-09 20:37:05 +02:00
|
|
|
/// - Le corps du bloc peut contenir du code asynchrone (`.await`)
|
|
|
|
|
///
|
|
|
|
|
/// # Type de retour
|
|
|
|
|
///
|
|
|
|
|
/// La macro retourne un [`ActionHandler`] prêt à l'emploi.
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// ## Exemple 1 : Handler simple (retourne les données telles quelles)
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// ```ignore
|
|
|
|
|
/// use pmoupnp::action_handler;
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let handler = action_handler!(|data| {
|
|
|
|
|
/// Ok(data) // Retourne les données non modifiées
|
2025-10-09 20:37:05 +02:00
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// ## Exemple 2 : Handler qui calcule et modifie les données
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// ```ignore
|
2025-10-19 07:58:37 +02:00
|
|
|
/// use pmoupnp::{action_handler, get, set};
|
2025-10-09 20:37:05 +02:00
|
|
|
/// use pmoupnp::actions::ActionError;
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let handler = action_handler!(|mut data| {
|
|
|
|
|
/// // Extraire les valeurs avec la macro get!
|
|
|
|
|
/// let celsius: f64 = get!(data, "Celsius", f64);
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// // Calculer
|
|
|
|
|
/// let fahrenheit = celsius * 9.0 / 5.0 + 32.0;
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// // Insérer avec la macro set!
|
|
|
|
|
/// set!(data, "Fahrenheit", fahrenheit);
|
2025-10-16 21:06:29 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Ok(data) // Retourner les données modifiées
|
2025-10-09 20:37:05 +02:00
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
2025-10-16 21:06:29 +02:00
|
|
|
/// ## Exemple 3 : Handler avec logique métier asynchrone
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// ```ignore
|
2025-10-19 07:58:37 +02:00
|
|
|
/// use pmoupnp::{action_handler, get, set};
|
2025-10-09 20:37:05 +02:00
|
|
|
/// use pmoupnp::actions::ActionError;
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let handler = action_handler!(|mut data| {
|
|
|
|
|
/// // Lire l'URI
|
|
|
|
|
/// let uri: String = get!(data, "URI", String);
|
2025-10-16 21:06:29 +02:00
|
|
|
///
|
2025-10-09 20:37:05 +02:00
|
|
|
/// // Appel asynchrone à un service externe
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let metadata = external_service::fetch_metadata(&uri).await
|
2025-10-09 20:37:05 +02:00
|
|
|
/// .map_err(|e| ActionError::ExternalError(e.to_string()))?;
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// // Mettre à jour les données
|
|
|
|
|
/// set!(data, "Metadata", metadata);
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Ok(data)
|
2025-10-09 20:37:05 +02:00
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// ## Exemple 4 : Handler avec capture de contexte
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
|
|
|
|
/// ```ignore
|
2025-10-19 07:58:37 +02:00
|
|
|
/// use pmoupnp::{action_handler, get, set};
|
2025-10-09 20:37:05 +02:00
|
|
|
/// use pmoupnp::actions::ActionError;
|
|
|
|
|
/// use std::sync::Arc;
|
|
|
|
|
/// use tokio::sync::Mutex;
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// // Contexte partagé
|
2025-10-09 20:37:05 +02:00
|
|
|
/// let player_state = Arc::new(Mutex::new(PlayerState::Stopped));
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// let handler = action_handler!(|mut data| {
|
|
|
|
|
/// // Vérifier l'état
|
2025-10-09 20:37:05 +02:00
|
|
|
/// {
|
|
|
|
|
/// let state = player_state.lock().await;
|
|
|
|
|
/// if *state == PlayerState::Error {
|
2025-10-19 07:58:37 +02:00
|
|
|
/// return Err(ActionError::InvalidState("Player in error state".into()));
|
2025-10-09 20:37:05 +02:00
|
|
|
/// }
|
|
|
|
|
/// }
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// // Modifier l'état
|
2025-10-09 20:37:05 +02:00
|
|
|
/// {
|
|
|
|
|
/// let mut state = player_state.lock().await;
|
|
|
|
|
/// *state = PlayerState::Playing;
|
|
|
|
|
/// }
|
|
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// // Mettre à jour les données
|
|
|
|
|
/// set!(data, "TransportState", "PLAYING".to_string());
|
2025-10-09 20:37:05 +02:00
|
|
|
///
|
2025-10-19 07:58:37 +02:00
|
|
|
/// Ok(data)
|
2025-10-09 20:37:05 +02:00
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Notes d'implémentation
|
|
|
|
|
///
|
|
|
|
|
/// - Le bloc est automatiquement wrappé dans `async move`
|
|
|
|
|
/// - Les captures de variables sont déplacées (`move`)
|
|
|
|
|
/// - Le résultat est automatiquement boxé et arcé
|
2025-10-19 07:58:37 +02:00
|
|
|
/// - Utilisez les macros `get!` et `set!` pour manipuler facilement les données
|
2025-10-09 20:37:05 +02:00
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! action_handler {
|
2025-10-19 07:58:37 +02:00
|
|
|
(|$data:ident| $body:block) => {
|
|
|
|
|
std::sync::Arc::new(|$data: $crate::actions::ActionData| {
|
2025-10-09 20:37:05 +02:00
|
|
|
Box::pin(async move $body)
|
|
|
|
|
})
|
|
|
|
|
};
|
2025-10-19 10:06:04 +02:00
|
|
|
}
|