Gros refactoring
This commit is contained in:
@@ -1,59 +1,116 @@
|
||||
use std::fmt;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use tokio::sync::RwLock;
|
||||
use xmltree::Element;
|
||||
|
||||
use crate::{
|
||||
UpnpObject, UpnpObjectType,
|
||||
object_trait::UpnpXml,
|
||||
state_variables::{StateVarInstance, StateVariable, UpnpVariable},
|
||||
variable_types::StateValue,
|
||||
object_trait::{UpnpInstance, UpnpObject}, state_variables::{StateVarInstance, StateVariable, UpnpVariable}, variable_types::{StateValue, StateValueError, UpnpVarType}, UpnpObjectType, UpnpTyped, UpnpTypedInstance, UpnpTypedObject
|
||||
};
|
||||
|
||||
impl UpnpVariable for StateVarInstance {
|
||||
fn get_definition(&self) -> &StateVariable {
|
||||
return &self.definition;
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpXml for StateVarInstance {
|
||||
fn to_xml_element(&self) -> Element {
|
||||
self.get_definition().to_xml_element()
|
||||
return &self.model;
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpObject for StateVarInstance {
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType {
|
||||
return &self.object;
|
||||
async fn to_xml_element(&self) -> Element {
|
||||
self.get_definition().to_xml_element().await
|
||||
}
|
||||
}
|
||||
|
||||
impl StateVarInstance {
|
||||
pub fn new(from: &StateVariable) -> Self {
|
||||
impl UpnpVarType for StateVarInstance {
|
||||
fn as_state_var_type(&self) -> crate::variable_types::StateVarType {
|
||||
self.get_definition().as_state_var_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpInstance for StateVarInstance {
|
||||
type Model = StateVariable;
|
||||
|
||||
fn new(from: &StateVariable) -> Self {
|
||||
Self {
|
||||
object: UpnpObjectType {
|
||||
name: from.object.name.clone(),
|
||||
object_type: "StateVarInstance".to_string(),
|
||||
},
|
||||
definition: from.clone(),
|
||||
value: from.get_default(),
|
||||
old_value: from.get_default(),
|
||||
last_modified: Utc::now(),
|
||||
last_notification: Utc::now(),
|
||||
model: from.clone(),
|
||||
value: RwLock::new(from.get_default()),
|
||||
old_value: RwLock::new(from.get_default()),
|
||||
last_modified: RwLock::new(Utc::now()),
|
||||
last_notification: RwLock::new(Utc::now()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, new_value: StateValue) {
|
||||
self.old_value = self.value.clone();
|
||||
self.value = new_value;
|
||||
self.last_modified = Utc::now(); // mise à jour automatique
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpTyped for StateVarInstance {
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType {
|
||||
return &self.object;
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpTypedInstance for StateVarInstance {
|
||||
|
||||
fn get_model(&self) -> &Self::Model {
|
||||
&self.model
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StateVarInstance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("StateVarInstance")
|
||||
.field("object", &self.object)
|
||||
.field("model", &self.model)
|
||||
.field("value", &self.value)
|
||||
.field("old_value", &self.old_value)
|
||||
.field("last_modified", &self.last_modified)
|
||||
.field("last_notification", &self.last_notification)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for StateVarInstance {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
object: self.object.clone(),
|
||||
model: self.model.clone(),
|
||||
value: RwLock::new(self.value.blocking_read().clone()),
|
||||
old_value: RwLock::new(self.old_value.blocking_read().clone()),
|
||||
last_modified: RwLock::new(self.last_modified.blocking_read().clone()),
|
||||
last_notification: RwLock::new(self.last_notification.blocking_read().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StateVarInstance {
|
||||
pub async fn set_value(&self, new_value: StateValue) -> Result<(), StateValueError> {
|
||||
// Validation du type
|
||||
if self.as_state_var_type() != new_value.as_state_var_type() {
|
||||
return Err(StateValueError::TypeError(
|
||||
"Value type mismatch".to_string()
|
||||
));
|
||||
}
|
||||
|
||||
// Mise à jour avec les locks
|
||||
let mut old_val = self.old_value.write().await;
|
||||
let mut val = self.value.write().await;
|
||||
let mut modified = self.last_modified.write().await;
|
||||
|
||||
*old_val = val.clone();
|
||||
*val = new_value;
|
||||
*modified = Utc::now();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Accès à la valeur
|
||||
pub fn value(&self) -> &StateValue {
|
||||
&self.value
|
||||
pub fn value(&self) -> StateValue {
|
||||
self.value.blocking_read().clone()
|
||||
}
|
||||
|
||||
/// Accès au timestamp
|
||||
pub fn last_modified(&self) -> DateTime<Utc> {
|
||||
self.last_modified
|
||||
self.last_modified.blocking_read().clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,22 @@ mod errors;
|
||||
mod instance_methods;
|
||||
mod variable_methods;
|
||||
mod var_set_methods;
|
||||
mod var_inst_set_methods;
|
||||
mod variable_trait;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub use crate::state_variables::variable_trait::UpnpVariable;
|
||||
use bevy_reflect::Reflect;
|
||||
use chrono::{DateTime, Utc};
|
||||
pub use errors::StateVariableError;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
UpnpObjectType,
|
||||
value_ranges::ValueRange,
|
||||
variable_types::{StateValue, StateVarType},
|
||||
value_ranges::ValueRange, variable_types::{StateValue, StateVarType}, UpnpObjectSet, UpnpObjectType, UpnpSet
|
||||
};
|
||||
|
||||
/// Type pour les fonctions de condition d'événement
|
||||
@@ -46,16 +46,16 @@ pub struct StateVariable {
|
||||
marshal: Option<ValueSerializer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct StateVariableSet {
|
||||
instances: HashMap<String, StateVariable>,
|
||||
}
|
||||
pub type StateVariableSet = UpnpObjectSet<StateVariable>;
|
||||
|
||||
pub struct StateVarInstance {
|
||||
object: UpnpObjectType,
|
||||
definition: StateVariable,
|
||||
value: StateValue,
|
||||
old_value: StateValue,
|
||||
last_modified: DateTime<Utc>,
|
||||
last_notification: DateTime<Utc>,
|
||||
model: StateVariable,
|
||||
value: RwLock<StateValue>,
|
||||
old_value: RwLock<StateValue>,
|
||||
last_modified: RwLock<DateTime<Utc>>,
|
||||
last_notification: RwLock<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
pub type StateVarInstanceSet = UpnpObjectSet<StateVarInstance>;
|
||||
|
||||
|
||||
33
pmoupnp/src/state_variables/var_inst_set_methods.rs
Normal file
33
pmoupnp/src/state_variables/var_inst_set_methods.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
use xmltree::{Element, XMLNode};
|
||||
|
||||
use crate::{state_variables::{StateVarInstance, StateVarInstanceSet, StateVariableSet}, UpnInstanceSet, UpnpObject, UpnpTyped};
|
||||
|
||||
use crate::UpnpInstance;
|
||||
|
||||
impl UpnpObject for StateVarInstanceSet {
|
||||
async fn to_xml_element(&self) -> Element {
|
||||
let mut elem = Element::new("serviceStateTable");
|
||||
|
||||
for state_var in self.all().await {
|
||||
let state_var_elem = state_var.to_xml_element().await; // retourne un <stateVariable> complet
|
||||
elem.children.push(XMLNode::Element(state_var_elem));
|
||||
}
|
||||
|
||||
elem
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpInstance for StateVarInstanceSet {
|
||||
type Model = StateVariableSet;
|
||||
|
||||
fn new(_: &StateVariableSet) -> Self {
|
||||
Self { objects: RwLock::new(HashMap::new()) }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,25 @@
|
||||
use std::collections::HashMap;
|
||||
use xmltree::{Element, XMLNode};
|
||||
|
||||
use crate::{state_variables::{StateVariable, StateVariableSet}, UpnpObject};
|
||||
use crate::{object_trait::UpnpModel, state_variables::{StateVarInstanceSet, StateVariableSet}, UpnInstanceSet, UpnpObject};
|
||||
|
||||
impl StateVariableSet {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
instances: HashMap::new(),
|
||||
|
||||
impl UpnpObject for StateVariableSet {
|
||||
|
||||
async fn to_xml_element(&self) -> Element {
|
||||
let mut elem = Element::new("serviceStateTable");
|
||||
|
||||
for state_var in self.get_all() {
|
||||
let state_var_elem = state_var.to_xml_element().await; // retourne un <stateVariable> complet
|
||||
elem.children.push(XMLNode::Element(state_var_elem));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, instance: StateVariable) {
|
||||
self.instances.insert(instance.get_name().clone(), instance);
|
||||
elem
|
||||
}
|
||||
|
||||
pub fn contains(&self, name: &str) -> bool {
|
||||
self.instances.contains_key(name)
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<&StateVariable> {
|
||||
self.instances.get(name)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &StateVariable> {
|
||||
self.instances.values()
|
||||
}
|
||||
|
||||
pub fn all(&self) -> Vec<&StateVariable> {
|
||||
self.instances.values().collect()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl UpnpModel for StateVariableSet {
|
||||
type Instance = StateVarInstanceSet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
use std::{
|
||||
collections::HashMap, fmt, sync::{Arc, RwLock}
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
use xmltree::{Element, XMLNode};
|
||||
|
||||
use crate::{
|
||||
object_trait::UpnpXml, state_variables::{
|
||||
variable_trait::UpnpVariable, StateConditionFunc, StateVariable, StringValueParser, ValueSerializer
|
||||
}, value_ranges::ValueRange, variable_types::{StateValue, StateValueError, StateVarType, UpnpVarType}, UpnpObject, UpnpObjectType
|
||||
UpnpObjectType, UpnpTyped,
|
||||
object_trait::{UpnpModel, UpnpObject},
|
||||
state_variables::{
|
||||
StateConditionFunc, StateVarInstance, StateVariable, StringValueParser, ValueSerializer,
|
||||
variable_trait::UpnpVariable,
|
||||
},
|
||||
value_ranges::ValueRange,
|
||||
variable_types::{StateValue, StateValueError, StateVarType, UpnpVarType},
|
||||
};
|
||||
|
||||
impl UpnpXml for StateVariable {
|
||||
fn to_xml_element(&self) -> Element {
|
||||
impl UpnpTyped for StateVariable {
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType {
|
||||
&self.object
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpVarType for StateVariable {
|
||||
fn as_state_var_type(&self) -> StateVarType {
|
||||
self.value_type.as_state_var_type() // utilise ton From<&StateValue> existant
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpObject for StateVariable {
|
||||
async fn to_xml_element(&self) -> Element {
|
||||
// Création de l'élément racine <stateVariable>
|
||||
let mut root = Element::new("stateVariable");
|
||||
root.attributes.insert(
|
||||
@@ -39,16 +59,15 @@ impl UpnpXml for StateVariable {
|
||||
}
|
||||
|
||||
// <allowedValueList> si défini
|
||||
if let Ok(av) = self.allowed_values.read() {
|
||||
if !av.is_empty() {
|
||||
let mut list_elem = Element::new("allowedValueList");
|
||||
for val in av.iter() {
|
||||
let mut val_elem = Element::new("allowedValue");
|
||||
val_elem.children.push(XMLNode::Text(val.to_string()));
|
||||
list_elem.children.push(XMLNode::Element(val_elem));
|
||||
}
|
||||
root.children.push(XMLNode::Element(list_elem));
|
||||
let av = self.allowed_values.read().await;
|
||||
if !av.is_empty() {
|
||||
let mut list_elem = Element::new("allowedValueList");
|
||||
for val in av.iter() {
|
||||
let mut val_elem = Element::new("allowedValue");
|
||||
val_elem.children.push(XMLNode::Text(val.to_string()));
|
||||
list_elem.children.push(XMLNode::Element(val_elem));
|
||||
}
|
||||
root.children.push(XMLNode::Element(list_elem));
|
||||
}
|
||||
|
||||
// <allowedValueRange> si défini
|
||||
@@ -82,13 +101,10 @@ impl UpnpXml for StateVariable {
|
||||
|
||||
root
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl UpnpObject for StateVariable {
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType {
|
||||
return &self.object;
|
||||
}
|
||||
impl UpnpModel for StateVariable {
|
||||
type Instance = StateVarInstance;
|
||||
}
|
||||
|
||||
impl Clone for StateVariable {
|
||||
@@ -98,8 +114,7 @@ impl Clone for StateVariable {
|
||||
// si le lock est "poisoned" on panic - tu peux adapter la gestion si tu veux
|
||||
let guard = self
|
||||
.event_conditions
|
||||
.read()
|
||||
.expect("RwLock poisoned during clone");
|
||||
.blocking_read();
|
||||
// nécessite que Key: Clone, Value: Clone
|
||||
Arc::new(RwLock::new(guard.clone()))
|
||||
};
|
||||
@@ -107,8 +122,7 @@ impl Clone for StateVariable {
|
||||
let allowed_values_clone = {
|
||||
let guard = self
|
||||
.allowed_values
|
||||
.read()
|
||||
.expect("RwLock poisoned during clone");
|
||||
.blocking_read();
|
||||
Arc::new(RwLock::new(guard.clone()))
|
||||
};
|
||||
|
||||
@@ -142,7 +156,7 @@ impl fmt::Debug for StateVariable {
|
||||
"event_conditions",
|
||||
&format_args!(
|
||||
"len={}",
|
||||
self.event_conditions.read().map(|m| m.len()).unwrap_or(0)
|
||||
self.event_conditions.blocking_read().len()
|
||||
),
|
||||
)
|
||||
.field("description", &self.description)
|
||||
@@ -152,22 +166,30 @@ impl fmt::Debug for StateVariable {
|
||||
"allowed_values",
|
||||
&format_args!(
|
||||
"len={}",
|
||||
self.allowed_values.read().map(|v| v.len()).unwrap_or(0)
|
||||
self.allowed_values.blocking_read().len()
|
||||
),
|
||||
)
|
||||
.field("send_events", &self.send_events)
|
||||
.field("parse", &self.parse.as_ref().map(|_| "Some(StringValueParser)").unwrap_or("None"))
|
||||
.field("marshal", &self.marshal.as_ref().map(|_| "Some(ValueSerializer)").unwrap_or("None"))
|
||||
.field(
|
||||
"parse",
|
||||
&self
|
||||
.parse
|
||||
.as_ref()
|
||||
.map(|_| "Some(StringValueParser)")
|
||||
.unwrap_or("None"),
|
||||
)
|
||||
.field(
|
||||
"marshal",
|
||||
&self
|
||||
.marshal
|
||||
.as_ref()
|
||||
.map(|_| "Some(ValueSerializer)")
|
||||
.unwrap_or("None"),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpVarType for StateVariable {
|
||||
fn as_state_var_type(&self) -> StateVarType {
|
||||
self.value_type // utilise ton From<&StateValue> existant
|
||||
}
|
||||
}
|
||||
|
||||
impl UpnpVariable for StateVariable {
|
||||
fn get_definition(&self) -> &StateVariable {
|
||||
return self;
|
||||
@@ -276,18 +298,18 @@ impl StateVariable {
|
||||
|
||||
pub fn add_event_condition(&self, name: String, func: StateConditionFunc) {
|
||||
// on lock en écriture
|
||||
let mut guard = self.event_conditions.write().unwrap();
|
||||
let mut guard = self.event_conditions.blocking_write();
|
||||
guard.insert(name, func);
|
||||
// le lock est automatiquement relâché ici (RAII)
|
||||
}
|
||||
|
||||
pub fn remove_event_condition(&self, name: &str) {
|
||||
let mut guard = self.event_conditions.write().unwrap();
|
||||
let mut guard = self.event_conditions.blocking_write();
|
||||
guard.remove(name);
|
||||
}
|
||||
|
||||
pub fn clear_event_conditions(&mut self) {
|
||||
let mut guard = self.event_conditions.write().unwrap();
|
||||
let mut guard = self.event_conditions.blocking_write();
|
||||
guard.clear()
|
||||
}
|
||||
|
||||
@@ -312,8 +334,7 @@ impl StateVariable {
|
||||
pub fn extend_allowed_values(&mut self, values: &[StateValue]) -> Result<(), StateValueError> {
|
||||
let mut av = self
|
||||
.allowed_values
|
||||
.write()
|
||||
.map_err(|_| StateValueError::TypeError("Lock poisoned".to_string()))?;
|
||||
.blocking_write();
|
||||
|
||||
for v in values {
|
||||
if self.as_state_var_type() == v.as_state_var_type() {
|
||||
@@ -331,8 +352,7 @@ impl StateVariable {
|
||||
pub fn push_allowed_value(&mut self, value: &StateValue) -> Result<(), StateValueError> {
|
||||
let mut av = self
|
||||
.allowed_values
|
||||
.write()
|
||||
.map_err(|_| StateValueError::TypeError("Lock poisoned".to_string()))?;
|
||||
.blocking_write();
|
||||
|
||||
if self.as_state_var_type() == value.as_state_var_type() {
|
||||
av.push(value.clone());
|
||||
|
||||
@@ -3,46 +3,199 @@ use crate::{
|
||||
variable_types::{StateValue, UpnpVarType},
|
||||
};
|
||||
|
||||
/// Trait pour accéder aux propriétés et contraintes d'une variable UPnP.
|
||||
///
|
||||
/// Ce trait fournit une interface uniforme pour interroger les métadonnées,
|
||||
/// contraintes et comportements d'une variable UPnP, qu'il s'agisse d'une
|
||||
/// définition ([`StateVariable`]) ou d'une instance ([`StateVarInstance`]).
|
||||
///
|
||||
/// # Architecture
|
||||
///
|
||||
/// Le trait utilise le pattern "trait avec implémentation par défaut" :
|
||||
/// - Une seule méthode requise : [`get_definition`](Self::get_definition)
|
||||
/// - Toutes les autres méthodes sont implémentées par défaut en déléguant à la définition
|
||||
///
|
||||
/// Cela permet une interface cohérente entre modèles et instances sans duplication de code.
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpVariable
|
||||
/// ├─> StateVariable (get_definition() retourne self)
|
||||
/// └─> StateVarInstance (get_definition() retourne self.definition)
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn display_variable_info<V: UpnpVariable>(var: &V) {
|
||||
/// println!("Variable: {}", var.get_definition().get_name());
|
||||
///
|
||||
/// if var.has_default() {
|
||||
/// println!("Default: {:?}", var.get_default());
|
||||
/// }
|
||||
///
|
||||
/// if var.has_range() {
|
||||
/// println!("Has range constraints");
|
||||
/// }
|
||||
///
|
||||
/// if var.has_allowed_values() {
|
||||
/// println!("Has allowed values list");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait UpnpVariable {
|
||||
/// Retourne une référence vers la définition de la variable.
|
||||
///
|
||||
/// Cette méthode est la base de toutes les autres méthodes du trait.
|
||||
///
|
||||
/// # Implementation
|
||||
///
|
||||
/// - Pour [`StateVariable`] : retourne `self`
|
||||
/// - Pour [`StateVarInstance`] : retourne `self.definition`
|
||||
fn get_definition(&self) -> &StateVariable;
|
||||
|
||||
/// Indique si la variable a un pas (step) défini.
|
||||
///
|
||||
/// Le pas définit l'incrément minimal entre deux valeurs valides pour
|
||||
/// les types numériques.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si un pas est défini, `false` sinon.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// if var.has_step() {
|
||||
/// println!("Step: {:?}", var.get_step());
|
||||
/// }
|
||||
/// ```
|
||||
fn has_step(&self) -> bool {
|
||||
return self.get_definition().step.is_some();
|
||||
self.get_definition().step.is_some()
|
||||
}
|
||||
|
||||
/// Retourne le pas (step) de la variable s'il est défini.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Some(StateValue)` si un pas est défini
|
||||
/// - `None` sinon
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`has_step`](Self::has_step) pour tester l'existence
|
||||
fn get_step(&self) -> Option<StateValue> {
|
||||
return self.get_definition().step.clone();
|
||||
self.get_definition().step.clone()
|
||||
}
|
||||
|
||||
/// Indique si la variable a une plage de valeurs (range) définie.
|
||||
///
|
||||
/// La plage définit les valeurs minimale et maximale acceptables.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si une plage est définie, `false` sinon.
|
||||
fn has_range(&self) -> bool {
|
||||
return self.get_definition().value_range.is_some();
|
||||
self.get_definition().value_range.is_some()
|
||||
}
|
||||
|
||||
/// Indique si la variable est modifiable.
|
||||
///
|
||||
/// Une variable non modifiable est en lecture seule.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si la variable peut être modifiée, `false` sinon.
|
||||
fn is_modifiable(&self) -> bool {
|
||||
return self.get_definition().modifiable;
|
||||
self.get_definition().modifiable
|
||||
}
|
||||
|
||||
/// Indique si la variable a des conditions d'événement définies.
|
||||
///
|
||||
/// Les conditions d'événement déterminent quand des notifications
|
||||
/// doivent être envoyées lors de changements de valeur.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si au moins une condition d'événement existe, `false` sinon.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Retourne `false` si le lock est empoisonné (poisoned).
|
||||
fn has_event_conditions(&self) -> bool {
|
||||
return self.get_definition().event_conditions.read().unwrap().len() > 0;
|
||||
let guard = self.get_definition().event_conditions.blocking_read();
|
||||
!guard.is_empty()
|
||||
}
|
||||
|
||||
/// Vérifie si une condition d'événement spécifique existe.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - Le nom de la condition à rechercher
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si la condition existe, `false` sinon.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Retourne `false` si le lock est empoisonné (poisoned).
|
||||
fn has_event_condition(&self, name: &String) -> bool {
|
||||
let guard = self.get_definition().event_conditions.read().unwrap();
|
||||
return guard.contains_key(name);
|
||||
let guard = self.get_definition().event_conditions.blocking_read();
|
||||
guard.contains_key(name)
|
||||
}
|
||||
|
||||
/// Indique si la variable a une description non vide.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si une description existe et n'est pas vide, `false` sinon.
|
||||
fn has_description(&self) -> bool {
|
||||
return !String::is_empty(&self.get_definition().description);
|
||||
!self.get_definition().description.is_empty()
|
||||
}
|
||||
|
||||
/// Retourne la description de la variable.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// La description sous forme de `String`. Peut être vide.
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`has_description`](Self::has_description) pour tester si non vide
|
||||
fn get_description(&self) -> String {
|
||||
return self.get_definition().description.clone();
|
||||
self.get_definition().description.clone()
|
||||
}
|
||||
|
||||
/// Indique si la variable a une valeur par défaut définie explicitement.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si une valeur par défaut est explicitement définie, `false` sinon.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Même si cette méthode retourne `false`, [`get_default`](Self::get_default)
|
||||
/// retournera toujours une valeur (la valeur par défaut du type).
|
||||
fn has_default(&self) -> bool {
|
||||
return self.get_definition().default_value.is_some();
|
||||
self.get_definition().default_value.is_some()
|
||||
}
|
||||
|
||||
/// Retourne la valeur par défaut de la variable.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// La valeur par défaut. Si aucune valeur par défaut n'est explicitement
|
||||
/// définie, retourne la valeur par défaut du type de la variable
|
||||
/// (ex: 0 pour les entiers, chaîne vide pour String, etc.).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let default = var.get_default();
|
||||
/// println!("Default value: {:?}", default);
|
||||
/// ```
|
||||
fn get_default(&self) -> StateValue {
|
||||
self.get_definition()
|
||||
.default_value
|
||||
@@ -50,23 +203,99 @@ pub trait UpnpVariable {
|
||||
.unwrap_or_else(|| self.get_definition().as_state_var_type().default_value())
|
||||
}
|
||||
|
||||
/// Indique si la variable a une liste de valeurs autorisées.
|
||||
///
|
||||
/// Lorsqu'une liste de valeurs autorisées est définie, seules ces valeurs
|
||||
/// sont acceptables pour la variable.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si une liste non vide de valeurs autorisées existe, `false` sinon.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Retourne `false` si le lock est empoisonné (poisoned).
|
||||
fn has_allowed_values(&self) -> bool {
|
||||
return self.get_definition().allowed_values.read().unwrap().len() > 0;
|
||||
let guard = self.get_definition()
|
||||
.allowed_values
|
||||
.blocking_read();
|
||||
|
||||
!guard.is_empty()
|
||||
}
|
||||
|
||||
fn is_an_allowed_values(&self, value: &StateValue) -> bool {
|
||||
let guard = self.get_definition().allowed_values.read().unwrap();
|
||||
return guard.contains(value);
|
||||
/// Vérifie si une valeur fait partie des valeurs autorisées.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `value` - La valeur à vérifier
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si la valeur est dans la liste des valeurs autorisées, `false` sinon.
|
||||
/// Retourne également `false` si aucune liste de valeurs autorisées n'est définie
|
||||
/// ou si le lock est empoisonné.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let value = StateValue::String("ON".to_string());
|
||||
/// if var.is_an_allowed_value(&value) {
|
||||
/// println!("Value is allowed");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Si aucune liste de valeurs autorisées n'est définie, cette méthode
|
||||
/// retourne `false`. Utilisez [`has_allowed_values`](Self::has_allowed_values)
|
||||
/// pour distinguer "pas de liste" de "valeur non autorisée".
|
||||
fn is_an_allowed_value(&self, value: &StateValue) -> bool {
|
||||
let guard = self.get_definition()
|
||||
.allowed_values
|
||||
.blocking_read();
|
||||
|
||||
guard.contains(value)
|
||||
}
|
||||
|
||||
/// Indique si la variable envoie des notifications d'événement.
|
||||
///
|
||||
/// Les notifications d'événement sont envoyées aux abonnés lorsque
|
||||
/// la valeur de la variable change.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si les notifications sont activées, `false` sinon.
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`has_event_conditions`](Self::has_event_conditions) pour vérifier
|
||||
/// les conditions d'envoi d'événements
|
||||
fn is_sending_notification(&self) -> bool {
|
||||
self.get_definition().send_events
|
||||
}
|
||||
|
||||
/// Indique si la variable a un parser de valeur personnalisé.
|
||||
///
|
||||
/// Un parser personnalisé est utilisé pour convertir des chaînes de
|
||||
/// caractères en valeurs typées. Disponible uniquement pour les variables
|
||||
/// de type String.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si un parser est défini, `false` sinon.
|
||||
fn has_value_parser(&self) -> bool {
|
||||
self.get_definition().parse.is_some()
|
||||
}
|
||||
|
||||
/// Indique si la variable a un marshaler de valeur personnalisé.
|
||||
///
|
||||
/// Un marshaler personnalisé est utilisé pour sérialiser des valeurs
|
||||
/// en chaînes de caractères. Disponible uniquement pour les variables
|
||||
/// de type String.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si un marshaler est défini, `false` sinon.
|
||||
fn has_value_marshaler(&self) -> bool {
|
||||
self.get_definition().marshal.is_some()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user