//! Helpers et macros pour faciliter l'écriture de handlers d'actions. //! //! Ce module fournit des fonctions utilitaires et des macros pour simplifier //! la manipulation de [`ActionData`](crate::actions::ActionData) dans les handlers. //! //! # Fonctions utilitaires //! //! - [`get_value`] : Extrait une valeur typée depuis ActionData //! - [`set_value`] : Insère une valeur dans ActionData //! //! # Macros //! //! - [`get!`](crate::get) : Macro pour extraire facilement une valeur //! - [`set!`](crate::set) : Macro pour insérer facilement une valeur //! //! # Examples //! //! ```rust //! use pmoupnp::{action_handler, get, set}; //! use pmoupnp::actions::ActionError; //! //! let handler = action_handler!(|mut data| { //! // Extraction avec macro //! let celsius: f64 = get!(data, "Celsius", f64); //! //! // Calcul //! let fahrenheit = celsius * 9.0 / 5.0 + 32.0; //! //! // Insertion avec macro //! set!(data, "Fahrenheit", fahrenheit); //! //! Ok(data) //! }); //! ``` use bevy_reflect::Reflect; use crate::actions::{ActionData, ActionError}; /// Extrait une valeur typée depuis ActionData. /// /// Cette fonction permet d'extraire une valeur `Box` depuis /// ActionData et de la convertir vers le type concret attendu. /// /// # Type Parameters /// /// * `T` - Le type concret attendu (doit implémenter `Reflect + Clone`) /// /// # Arguments /// /// * `data` - Référence vers ActionData /// * `key` - Clé de la valeur à extraire /// /// # Returns /// /// `Ok(T)` si la valeur existe et peut être convertie vers `T`, /// `Err(ActionError)` sinon. /// /// # Errors /// /// Retourne `ActionError::ArgumentError` si : /// - La clé n'existe pas dans ActionData /// - La valeur ne peut pas être convertie vers le type `T` /// /// # Examples /// /// ```rust /// use pmoupnp::actions::{ActionData, get_value}; /// use std::collections::HashMap; /// /// let mut data: ActionData = HashMap::new(); /// data.insert("Volume".to_string(), Box::new(50u32)); /// /// let volume: u32 = get_value(&data, "Volume").unwrap(); /// assert_eq!(volume, 50); /// ``` pub fn get_value( data: &ActionData, key: &str ) -> Result { data.get(key) .and_then(|boxed| boxed.as_any().downcast_ref::()) .cloned() .ok_or_else(|| ActionError::ArgumentError(format!( "Argument '{}' not found or type mismatch", key ))) } /// Insère une valeur dans ActionData. /// /// Cette fonction convertit automatiquement la valeur en `Box` /// et l'insère dans ActionData. /// /// # Type Parameters /// /// * `T` - Le type de la valeur (doit implémenter `Reflect + 'static`) /// /// # Arguments /// /// * `data` - Référence mutable vers ActionData /// * `key` - Clé pour la valeur (convertie en `String`) /// * `value` - Valeur à insérer /// /// # Examples /// /// ```rust /// use pmoupnp::actions::{ActionData, set_value}; /// use std::collections::HashMap; /// /// let mut data: ActionData = HashMap::new(); /// set_value(&mut data, "Volume", 75u32); /// /// // Vérifier l'insertion /// use pmoupnp::actions::get_value; /// let volume: u32 = get_value(&data, "Volume").unwrap(); /// assert_eq!(volume, 75); /// ``` pub fn set_value( data: &mut ActionData, key: impl Into, value: T ) { data.insert(key.into(), Box::new(value)); } /// Convertit une valeur Reflect en chaîne lisible pour les logs/SOAP. /// /// Cette fonction réalise une tentative de conversion vers les types /// primitifs les plus courants (String, entiers, flottants, bool). /// Si aucune correspondance n'est trouvée, elle utilise `ReflectRef` /// pour fournir une représentation `Debug` générique. pub fn reflect_to_string(value: &dyn Reflect) -> String { if let Some(v) = value.as_any().downcast_ref::() { v.clone() } else if let Some(v) = value.as_any().downcast_ref::<&str>() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { v.to_string() } else if let Some(v) = value.as_any().downcast_ref::() { if *v { "1".to_string() } else { "0".to_string() } } else { "".to_string() } } /// Macro pour extraire facilement une valeur depuis ActionData. /// /// Cette macro simplifie l'utilisation de [`get_value`] en gérant /// automatiquement la propagation d'erreur avec `?`. /// /// # Syntaxe /// /// ```ignore /// get!(data, "key", Type) /// ``` /// /// # Arguments /// /// * `data` - Expression évaluant à `&ActionData` /// * `key` - Clé de la valeur (expression évaluant à `&str`) /// * `type` - Type concret attendu /// /// # Returns /// /// La valeur de type `Type` si elle existe et peut être convertie, /// sinon propage l'erreur avec `?`. /// /// # Examples /// /// ```ignore /// use pmoupnp::{get, action_handler}; /// use pmoupnp::actions::ActionError; /// /// let handler = action_handler!(|data| { /// let volume: u32 = get!(data, "Volume", u32); /// let name: String = get!(data, "Name", String); /// /// // Utiliser les valeurs... /// /// Ok(data) /// }); /// ``` #[macro_export] macro_rules! get { ($data:expr, $key:expr, $type:ty) => { $crate::actions::get_value::<$type>($data, $key)? }; } /// Macro pour insérer facilement une valeur dans ActionData. /// /// Cette macro simplifie l'utilisation de [`set_value`] pour /// insérer des valeurs dans ActionData. /// /// # Syntaxe /// /// ```ignore /// set!(data, "key", value) /// ``` /// /// # Arguments /// /// * `data` - Expression évaluant à `&mut ActionData` /// * `key` - Clé pour la valeur (expression évaluant vers `String`) /// * `value` - Valeur à insérer (doit implémenter `Reflect + 'static`) /// /// # Examples /// /// ```ignore /// use pmoupnp::{set, action_handler}; /// /// let handler = action_handler!(|mut data| { /// set!(data, "Result", 42u32); /// set!(data, "Message", "Success".to_string()); /// /// Ok(data) /// }); /// ``` #[macro_export] macro_rules! set { ($data:expr, $key:expr, $value:expr) => { $crate::actions::set_value($data, $key, $value) }; }