fin du travail précédent par chatgpt plus

This commit is contained in:
2025-10-19 10:06:04 +02:00
parent ebd66cd981
commit 079093f447
8 changed files with 222 additions and 318 deletions

View File

@@ -25,17 +25,15 @@
//! use std::sync::Arc;
//!
//! // Créer un handler avec la macro
//! let handler = action_handler!(|instance, data| {
//! let handler = action_handler!(|mut data| {
//! // Traiter les données
//! Ok::<(), ActionError>(())
//! Ok::<_, ActionError>(data)
//! });
//!
//! // Ou manuellement
//! use pmoupnp::actions::{ActionData, ActionHandler, ActionInstance};
//! let manual_handler: ActionHandler = Arc::new(|instance, data| {
//! Box::pin(async move {
//! Ok::<(), ActionError>(())
//! })
//! use pmoupnp::actions::{ActionData, ActionHandler};
//! let manual_handler: ActionHandler = Arc::new(|data| {
//! Box::pin(async move { Ok::<_, ActionError>(data) })
//! });
//! ```
@@ -299,4 +297,4 @@ macro_rules! action_handler {
Box::pin(async move $body)
})
};
}
}

View File

@@ -1,6 +1,9 @@
use std::sync::Arc;
use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use tracing::{debug, trace};
use bevy_reflect::Reflect;
use xmltree::{Element, XMLNode};
use crate::{
@@ -16,6 +19,7 @@ use crate::actions::{
ActionInstance,
ArgInstanceSet,
};
use crate::variable_types::StateValue;
impl UpnpObject for ActionInstance {
fn to_xml_element(&self) -> Element {
@@ -130,156 +134,135 @@ impl ActionInstance {
&self.arguments // ⬅️ Retourne les INSTANCES, pas les modèles !
}
/// Récupère les valeurs de tous les arguments de sortie (OUT).
/// Construit un [`ActionData`] initial à partir des variables d'état liées.
///
/// Cette méthode collecte automatiquement les valeurs actuelles de toutes
/// les variables d'état liées aux arguments OUT et les retourne dans un
/// [`ActionData`] indexé par le nom de chaque argument.
///
/// # Returns
///
/// Un [`ActionData`] contenant les paires (nom_argument, valeur_variable) pour
/// tous les arguments de sortie qui ont une variable d'instance liée.
///
/// # Examples
///
/// ```rust
/// # use pmoupnp::actions::{Action, ActionInstance};
/// # use pmoupnp::UpnpInstance;
/// # use std::sync::Arc;
/// let action = Action::new("GetVolume".to_string());
/// let instance = Arc::new(ActionInstance::new(&action));
///
/// // Récupérer automatiquement toutes les valeurs OUT
/// let output = instance.get_out_values();
///
/// // Afficher les résultats
/// for (arg_name, value) in output.iter() {
/// println!("{} = {:?}", arg_name, value);
/// }
/// ```
///
/// # Notes
///
/// - Seuls les arguments marqués comme OUT sont inclus
/// - Les arguments sans variable d'instance liée sont ignorés
/// - Le nom de l'argument (pas le nom de la variable) est utilisé comme clé
/// - Cette méthode est utilisée par le handler par défaut
pub fn get_out_values(&self) -> ActionData {
use std::collections::HashMap;
use crate::UpnpTypedInstance;
let mut result = HashMap::new();
/// Chaque argument lié à une [`StateVarInstance`](crate::state_variables::StateVarInstance)
/// voit sa valeur courante convertie en [`Reflect`](bevy_reflect::Reflect) pour alimenter
/// le handler de l'action.
fn build_action_data(&self) -> ActionData {
let mut data = HashMap::new();
for arg_inst in self.arguments.all() {
let arg_model = arg_inst.as_ref().get_model();
if arg_model.is_out() {
if let Some(var_inst) = arg_inst.get_variable_instance() {
result.insert(arg_inst.get_name().to_string(), var_inst.value());
if let Some(var_inst) = arg_inst.get_variable_instance() {
let name = arg_inst.get_name().to_string();
let reflect_value = var_inst.to_reflect();
data.insert(name, reflect_value);
}
}
data
}
/// Fusionne les valeurs SOAP IN dans l'[`ActionData`] existant.
///
/// Seuls les arguments marqués comme IN sont considérés. Les valeurs sont
/// converties depuis [`StateValue`] vers `Reflect` pour les handlers.
fn merge_soap_inputs(
&self,
action_data: &mut ActionData,
soap_data: &HashMap<String, StateValue>,
) -> HashSet<String> {
let mut updated = HashSet::new();
for (arg_name, state_value) in soap_data.iter() {
if let Some(arg_inst) = self.argument(arg_name) {
if arg_inst.get_model().is_in() {
action_data.insert(arg_name.clone(), state_value.to_reflect());
updated.insert(arg_name.clone());
}
}
}
Arc::new(result)
updated
}
/// Exécute l'action avec les données fournies.
///
/// Cette méthode :
/// 1. Stocke les valeurs IN dans les variables liées
/// 2. Exécute le handler (qui peut accéder aux valeurs IN via les variables)
/// 3. Collecte automatiquement les valeurs OUT via [`get_out_values()`](Self::get_out_values)
/// 4. Retourne les résultats
///
/// # Arguments
///
/// * `data` - Données d'entrée de l'action (arguments IN)
///
/// # Returns
///
/// Un `Future` qui se résout en `Result<ActionData, ActionError>` :
/// - `Ok(ActionData)` contenant les résultats (arguments OUT) si le handler réussit
/// - `Err(ActionError)` si le handler échoue
///
/// # Errors
///
/// Retourne une erreur si le handler retourne `Err(ActionError)`.
///
/// # Fonctionnement
///
/// 1. Pour chaque argument IN, la valeur fournie dans `data` est stockée dans la
/// variable d'état liée à cet argument
/// 2. Le handler est appelé avec l'instance (il peut lire les valeurs IN via
/// `argument.get_variable_instance().value()`)
/// 3. Le handler modifie les variables selon ses besoins et retourne `Ok(())` ou `Err(...)`
/// 4. Si le handler réussit, `run()` collecte automatiquement toutes les valeurs
/// des arguments marqués comme OUT
///
/// # Examples
///
/// ```rust,no_run
/// # use pmoupnp::actions::{Action, ActionData, ActionInstance};
/// # use pmoupnp::UpnpInstance;
/// # use std::collections::HashMap;
/// # use std::sync::Arc;
/// # async fn example() {
/// let action = Action::new("SetVolume".to_string());
/// let instance = Arc::new(ActionInstance::new(&action));
///
/// // Préparer les données d'entrée
/// let mut input = HashMap::new();
/// input.insert("DesiredVolume".to_string(),
/// pmoupnp::variable_types::StateValue::UI2(50));
/// let input_data = Arc::new(input);
///
/// // Exécuter l'action
/// // 1. run() stocke DesiredVolume=50 dans la variable liée
/// // 2. Le handler lit la valeur et fait son travail
/// // 3. run() retourne automatiquement les valeurs OUT
/// match instance.run(input_data).await {
/// Ok(output_data) => {
/// // Traiter les résultats
/// for (key, value) in output_data.iter() {
/// println!("{} = {:?}", key, value);
/// }
/// }
/// Err(e) => {
/// eprintln!("Action failed: {:?}", e);
/// }
/// }
/// # }
/// ```
///
/// # Notes
///
/// - Les valeurs IN sont automatiquement stockées avant l'appel du handler
/// - Le handler n'a plus besoin de recevoir les données en paramètre
/// - Le handler modifie les variables et retourne `Ok(())` ou `Err(ActionError)`
/// - `run()` collecte automatiquement les OUT si le handler retourne `Ok(())`
/// - L'instance doit être wrappée dans un `Arc` pour être passée au handler
pub async fn run(self: Arc<Self>, data: ActionData) -> Result<ActionData, crate::actions::ActionError> {
// Stocker les valeurs IN dans les variables liées
/// Sauvegarde les arguments IN dans les variables d'état (mode stateful uniquement).
async fn save_inputs_to_state_variables(
&self,
action_data: &ActionData,
updated_keys: &HashSet<String>,
) -> Result<(), crate::actions::ActionError> {
for arg_inst in self.arguments.all() {
let arg_model = arg_inst.as_ref().get_model();
if arg_model.is_in() {
if let Some(value) = data.get(arg_inst.get_name()) {
if let Some(var_inst) = arg_inst.get_variable_instance() {
var_inst.set_value(value.clone()).await.ok();
trace!(" IN {} = {:?}", arg_inst.get_name(), value);
if arg_inst.get_model().is_in() && updated_keys.contains(arg_inst.get_name()) {
if let Some(var_inst) = arg_inst.get_variable_instance() {
if let Some(reflect_value) = action_data.get(arg_inst.get_name()) {
let cloned = reflect_value
.as_ref()
.reflect_clone()
.map_err(|e| crate::actions::ActionError::ArgumentError(e.to_string()))?;
var_inst
.set_reflect_value(cloned)
.await
.map_err(|e| crate::actions::ActionError::SetError(e.to_string()))?;
}
}
}
}
Ok(())
}
/// Sauvegarde les arguments OUT dans les variables d'état (mode stateful uniquement).
async fn save_outputs_to_state_variables(
&self,
action_data: &ActionData,
) -> Result<(), crate::actions::ActionError> {
for arg_inst in self.arguments.all() {
if arg_inst.get_model().is_out() {
if let Some(var_inst) = arg_inst.get_variable_instance() {
if let Some(reflect_value) = action_data.get(arg_inst.get_name()) {
let cloned = reflect_value
.as_ref()
.reflect_clone()
.map_err(|e| crate::actions::ActionError::SetError(e.to_string()))?;
var_inst
.set_reflect_value(cloned)
.await
.map_err(|e| crate::actions::ActionError::SetError(e.to_string()))?;
}
}
}
}
Ok(())
}
/// Exécute l'action avec les données SOAP fournies.
///
/// Workflow unifié :
/// 1. Construire l'[`ActionData`] initial depuis les `StateVarInstance`
/// 2. Fusionner les valeurs IN issues du SOAP
/// 3. Si l'action est stateful : sauvegarder les IN dans les `StateVarInstance`
/// 4. Exécuter le handler
/// 5. Si l'action est stateful : sauvegarder les OUT dans les `StateVarInstance`
/// 6. Retourner l'[`ActionData`] pour la réponse SOAP
pub async fn run(
self: Arc<Self>,
soap_data: Arc<HashMap<String, StateValue>>,
) -> Result<ActionData, crate::actions::ActionError> {
// 1. Construire ActionData initial
let mut action_data = self.build_action_data();
// 2. Fusionner les valeurs SOAP IN
let updated_inputs = self.merge_soap_inputs(&mut action_data, &soap_data);
// 3. Sauvegarder les IN si stateful
if self.is_stateful() {
self.save_inputs_to_state_variables(&action_data, &updated_inputs)
.await?;
}
// 4. Exécuter le handler
let handler = self.model.handler().clone();
let instance_clone = self.clone();
let result_data = handler(action_data).await?;
// Exécuter le handler (il peut maintenant lire les valeurs IN depuis les variables)
handler(instance_clone).await?;
// 5. Sauvegarder les OUT si stateful
if self.is_stateful() {
self.save_outputs_to_state_variables(&result_data).await?;
}
// Collecter automatiquement les valeurs OUT si succès
debug!("✅ Action '{}' completed successfully, collecting outputs", self.get_name());
Ok(self.get_out_values())
// 6. Retourner les données pour la réponse SOAP
Ok(result_data)
}
}
@@ -310,4 +293,3 @@ mod tests {
}));
}
}

View File

@@ -1,6 +1,6 @@
use std::sync::Arc;
use tracing::{debug, info, trace};
use tracing::{info, trace};
use xmltree::{Element, XMLNode};
use crate::{
@@ -73,7 +73,11 @@ impl Action {
// Logger les arguments
for (key, value) in data.iter() {
trace!(" {} = {:?}", key, value);
trace!(
" {} = {}",
key,
crate::actions::reflect_to_string(value.as_ref())
);
}
// Retourner les données telles quelles
@@ -141,9 +145,9 @@ impl Action {
/// # use pmoupnp::action_handler;
/// let mut action = Action::new("Play".to_string());
///
/// let custom_handler = action_handler!(|instance, data| {
/// let custom_handler = action_handler!(|mut data| {
/// // Logique personnalisée
/// Ok::<(), ActionError>(())
/// Ok::<_, ActionError>(data)
/// });
///
/// action.set_handler(custom_handler);
@@ -184,6 +188,12 @@ impl Action {
self.stateful = stateful;
self
}
pub fn set_stateless(&mut self, stateless: bool) -> &mut Self {
self.stateful = !stateless;
self
}
/// Retourne `true` si l'action est stateful.
///

View File

@@ -35,7 +35,6 @@
use bevy_reflect::Reflect;
use crate::actions::{ActionData, ActionError};
use std::any::Any;
/// Extrait une valeur typée depuis ActionData.
///
@@ -58,7 +57,7 @@ use std::any::Any;
///
/// # Errors
///
/// Retourne `ActionError::ArgumentNotFound` si :
/// Retourne `ActionError::ArgumentError` si :
/// - La clé n'existe pas dans ActionData
/// - La valeur ne peut pas être convertie vers le type `T`
///
@@ -81,7 +80,10 @@ pub fn get_value<T: Reflect + Clone>(
data.get(key)
.and_then(|boxed| boxed.as_any().downcast_ref::<T>())
.cloned()
.ok_or_else(|| ActionError::ArgumentNotFound(key.to_string()))
.ok_or_else(|| ActionError::ArgumentError(format!(
"Argument '{}' not found or type mismatch",
key
)))
}
/// Insère une valeur dans ActionData.
@@ -121,6 +123,44 @@ pub fn set_value<T: Reflect + 'static>(
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::<String>() {
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::<u8>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<u16>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<u32>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<u64>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<i8>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<i16>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<i32>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<i64>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<f32>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<f64>() {
v.to_string()
} else if let Some(v) = value.as_any().downcast_ref::<bool>() {
if *v { "1".to_string() } else { "0".to_string() }
} else {
"<unsupported Reflect>".to_string()
}
}
/// Macro pour extraire facilement une valeur depuis ActionData.
///
/// Cette macro simplifie l'utilisation de [`get_value`] en gérant

View File

@@ -1,107 +1,3 @@
/// Macro pour créer facilement une action avec son handler.
///
/// Cette macro simplifie la création d'actions dynamiques en combinant
/// la création de l'action et l'assignation du handler en une seule expression.
///
/// # Syntaxe
///
/// ## Action stateful (défaut)
///
/// ```ignore
/// action!("ActionName", |data| {
/// // Handler code
/// Ok(data)
/// })
/// ```
///
/// ## Action stateless
///
/// ```ignore
/// action!("ActionName", stateless, |data| {
/// // Handler code
/// Ok(data)
/// })
/// ```
///
/// # Arguments
///
/// * `name` - Nom de l'action UPnP (chaîne littérale ou expression String)
/// * `stateless` - (Optionnel) Mot-clé pour marquer l'action comme stateless
/// * `|data| { ... }` - Closure du handler (voir [`action_handler!`](crate::action_handler))
///
/// # Type de retour
///
/// Retourne une `Action` configurée avec le handler spécifié.
///
/// # Examples
///
/// ## Action stateless simple
///
/// ```ignore
/// use pmoupnp::{action, get, set};
///
/// let convert = action!("ConvertTemp", stateless, |mut data| {
/// let celsius: f64 = get!(data, "Celsius", f64);
/// let fahrenheit = celsius * 9.0 / 5.0 + 32.0;
/// set!(data, "Fahrenheit", fahrenheit);
/// Ok(data)
/// });
/// ```
///
/// ## Action stateful (défaut)
///
/// ```ignore
/// use pmoupnp::{action, get, set};
///
/// let play = action!("Play", |mut data| {
/// let speed: String = get!(data, "Speed", String);
/// set!(data, "TransportState", "PLAYING".to_string());
/// Ok(data)
/// });
/// ```
///
/// ## Avec capture de contexte
///
/// ```ignore
/// use pmoupnp::{action, get, set};
/// use std::sync::Arc;
/// use tokio::sync::Mutex;
///
/// let state = Arc::new(Mutex::new(PlayerState::Stopped));
/// let state_clone = state.clone();
///
/// let play = action!("Play", |mut data| {
/// let mut player = state_clone.lock().await;
/// *player = PlayerState::Playing;
/// set!(data, "TransportState", "PLAYING".to_string());
/// Ok(data)
/// });
/// ```
///
/// # Notes
///
/// - Actions stateful (défaut) : mettent à jour les StateVarInstance (notifications)
/// - Actions stateless : pas de mise à jour des StateVarInstance (performances)
/// - Le handler capture les variables par `move`
/// - Utilisez [`get!`](crate::get) et [`set!`](crate::set) pour manipuler facilement les données
#[macro_export]
macro_rules! action {
// Action stateful (défaut)
($name:expr, |$data:ident| $body:block) => {{
let mut action = $crate::actions::Action::new($name.to_string());
action.set_handler($crate::action_handler!(|$data| $body));
action
}};
// Action stateless
($name:expr, stateless, |$data:ident| $body:block) => {{
let mut action = $crate::actions::Action::new($name.to_string());
action.set_stateful(false);
action.set_handler($crate::action_handler!(|$data| $body));
action
}};
}
/// Macro pour définir facilement une action UPnP.
///
/// Cette macro simplifie la création d'actions UPnP statiques en générant
@@ -208,12 +104,14 @@ macro_rules! action {
/// - Initialisation paresseuse via `Lazy` (thread-safe)
#[macro_export]
macro_rules! define_action {
// Variante sans arguments avec handler optionnel
(pub static $name:ident = $action_name:literal $(with handler $handler:expr)?) => {
// Variante sans arguments avec options `stateless` et handler
(pub static $name:ident = $action_name:literal $(stateless)? $(with handler $handler:expr)?) => {
pub static $name: once_cell::sync::Lazy<std::sync::Arc<$crate::actions::Action>> =
once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string());
define_action!(@maybe_stateless ac $(stateless)?);
$(
ac.set_handler($handler);
)?
@@ -222,8 +120,8 @@ macro_rules! define_action {
});
};
// Variante avec arguments et handler optionnel
(pub static $name:ident = $action_name:literal {
// Variante avec arguments, options `stateless` et handler
(pub static $name:ident = $action_name:literal $(stateless)? {
$(
$direction:ident $arg_name:literal => $var_ref:expr
),* $(,)?
@@ -234,6 +132,8 @@ macro_rules! define_action {
once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string());
define_action!(@maybe_stateless ac $(stateless)?);
$(
ac.add_argument(
define_action!(@arg $direction $arg_name, $var_ref)
@@ -247,6 +147,12 @@ macro_rules! define_action {
std::sync::Arc::new(ac)
});
};
(@maybe_stateless $ac:ident stateless) => {
$ac.set_stateful(false);
};
(@maybe_stateless $ac:ident) => {};
// Helper interne pour créer un argument d'entrée
(@arg in $name:literal, $var:expr) => {
@@ -362,29 +268,10 @@ macro_rules! define_action {
/// - La macro se développe en plusieurs appels à [`define_action!`]
#[macro_export]
macro_rules! define_actions {
// Variante avec arguments pour chaque action
(
$(
$name:ident = $action_name:literal {
$(
$direction:ident $arg_name:literal => $var_ref:expr
),* $(,)?
}
)*
) => {
$(
define_action! {
pub static $name = $action_name {
$($direction $arg_name => $var_ref),*
}
}
)*
};
// Variante mixte : actions avec et sans arguments
(
$(
$name:ident = $action_name:literal $({
$name:ident = $action_name:literal $(stateless)?
$({
$(
$direction:ident $arg_name:literal => $var_ref:expr
),* $(,)?
@@ -392,20 +279,15 @@ macro_rules! define_actions {
)*
) => {
$(
$(
define_action! {
pub static $name = $action_name {
define_action! {
pub static $name = $action_name
$(stateless)?
$(
{
$($direction $arg_name => $var_ref),*
}
}
)?
$(
// Cas sans accolades (action sans arguments)
#[allow(unused)]
define_action! {
pub static $name = $action_name
}
)?
)?
}
)*
};
}
}

View File

@@ -21,7 +21,7 @@ use std::sync::{Arc, RwLock};
pub use errors::ActionError;
pub use action_handler::{ActionData, ActionFuture, ActionHandler};
pub use handler_helpers::{get_value, set_value};
pub use handler_helpers::{get_value, reflect_to_string, set_value};
/// Action UPnP.
///

View File

@@ -37,8 +37,6 @@ use axum::{
use bevy_reflect::Reflect;
use std::{
collections::HashMap,
future::Future,
pin::Pin,
sync::{Arc, Mutex, RwLock},
time::Duration,
};
@@ -973,7 +971,6 @@ impl ServiceInstance {
///
/// Le résultat est déjà échappé XML-safe selon les normes UPnP.
fn reflect_to_string(value: &dyn Reflect) -> String {
use std::any::Any;
use bevy_reflect::ReflectRef;
// Essayer de downcaster vers des types primitifs courants
@@ -1290,21 +1287,8 @@ async fn control_handler(State(instance): State<Arc<ServiceInstance>>, body: Str
}
};
// Convertir les arguments SOAP (String) en ActionData (StateValue)
// D'abord, initialiser tous les arguments IN avec leurs valeurs par défaut
let mut action_data = HashMap::new();
for arg_inst in action_instance.arguments_set().all() {
let arg_model = arg_inst.as_ref().get_model();
if arg_model.is_in() {
if let Some(var_inst) = arg_inst.get_variable_instance() {
// Utiliser la valeur par défaut de la variable
let default_value = var_inst.value();
action_data.insert(arg_inst.get_name().to_string(), default_value);
}
}
}
// Puis, écraser avec les valeurs fournies dans le SOAP
// Convertir les arguments SOAP (String) en StateValue
let mut soap_values = HashMap::new();
for (arg_name, arg_value) in soap_action.args {
debug!("🔍 Processing SOAP arg: {} = '{}'", arg_name, arg_value);
// Trouver l'argument correspondant pour obtenir son type
@@ -1315,7 +1299,7 @@ async fn control_handler(State(instance): State<Arc<ServiceInstance>>, body: Str
match StateValue::from_string(&arg_value, &var_model.as_state_var_type()) {
Ok(value) => {
debug!("✅ Parsed {} = {:?}", arg_name, value);
action_data.insert(arg_name, value);
soap_values.insert(arg_name, value);
}
Err(e) => {
error!("❌ Failed to parse argument '{}': {:?}", arg_name, e);
@@ -1336,15 +1320,24 @@ async fn control_handler(State(instance): State<Arc<ServiceInstance>>, body: Str
}
}
let action_data = Arc::new(action_data);
let soap_values = Arc::new(soap_values);
// Exécuter l'action
match action_instance.run(action_data).await {
let action_instance_for_run = Arc::clone(&action_instance);
match action_instance_for_run.run(soap_values).await {
Ok(output_data) => {
// Convertir les StateValue en String pour SOAP
// Convertir ActionData (Reflect) → HashMap<String, String> pour SOAP
let mut soap_values = HashMap::new();
for (key, value) in output_data.iter() {
soap_values.insert(key.clone(), value.to_string());
for arg_inst in action_instance.arguments_set().all() {
let arg_model = arg_inst.as_ref().get_model();
if arg_model.is_out() {
if let Some(reflect_value) = output_data.get(arg_inst.get_name()) {
let soap_string = ServiceInstance::reflect_to_string(reflect_value.as_ref());
soap_values.insert(arg_inst.get_name().to_string(), soap_string);
}
}
}
// Construire la réponse SOAP

View File

@@ -6,7 +6,6 @@
use bevy_reflect::Reflect;
use crate::variable_types::{StateValue, StateValueError, StateVarType};
use std::any::Any;
impl StateValue {
/// Convertit la StateValue en une valeur Reflect.