Gros refactoring
This commit is contained in:
@@ -1,31 +1,151 @@
|
||||
//! ## Hiérarchie des traits
|
||||
//!
|
||||
//! ```text
|
||||
//! Clone + Debug
|
||||
//! └─> UpnpObject (trait de base)
|
||||
//! ├─> UpnpModel (modèles créant des instances)
|
||||
//! ├─> UpnpInstance (instances concrètes)
|
||||
//! ├─> UpnpTyped (objets avec nom et type)
|
||||
//! │ └─> UpnpTypedObject = UpnpObject + UpnpTyped
|
||||
//! │ └─> UpnpTypedInstance = UpnpTypedObject + UpnpInstance
|
||||
//! └─> UpnpSet (collections) + UpnpDeepClone
|
||||
//! ├─> UpnpModelSet = UpnpSet + UpnpModel
|
||||
//! └─> UpnInstanceSet = UpnpSet + UpnpInstance
|
||||
//!
|
||||
//! UpnpDeepClone (indépendant)
|
||||
//! ```
|
||||
//!
|
||||
//! ## Description des traits
|
||||
//!
|
||||
//! - **Traits de base** :
|
||||
//! - [`UpnpObject`] : Trait principal avec sérialisation XML/Markdown
|
||||
//! - [`UpnpDeepClone`] : Clonage profond (indépendant de la hiérarchie)
|
||||
//!
|
||||
//! - **Traits de spécialisation niveau 1** :
|
||||
//! - [`UpnpModel`] : Modèle pouvant créer des instances
|
||||
//! - [`UpnpInstance`] : Instance concrète créée depuis un modèle
|
||||
//! - [`UpnpTyped`] : Ajoute les informations de type et nom
|
||||
//! - [`UpnpSet`] : Marque un objet comme collection
|
||||
//!
|
||||
//! - **Traits combinés niveau 2** :
|
||||
//! - [`UpnpTypedObject`] : Objet typé (marker trait)
|
||||
//!
|
||||
//! - **Traits combinés niveau 3** :
|
||||
//! - [`UpnpTypedInstance`] : Instance typée (marker trait)
|
||||
//! - [`UpnpModelSet`] : Collection de modèles (marker trait)
|
||||
//! - [`UpnInstanceSet`] : Collection d'instances (marker trait)
|
||||
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
|
||||
use xmltree::{Element, EmitterConfig};
|
||||
|
||||
use crate::UpnpObjectType;
|
||||
|
||||
pub trait UpnpXml {
|
||||
fn to_xml_element(&self) -> Element;
|
||||
fn to_xml(&self) -> String {
|
||||
let elem = self.to_xml_element();
|
||||
/// Trait pour le clonage profond d'objets UPnP.
|
||||
///
|
||||
/// Contrairement au trait standard [`Clone`] qui peut effectuer un clonage superficiel
|
||||
/// (partage via `Arc`), ce trait garantit un clonage complet et indépendant de l'objet.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Ce trait est indépendant de la hiérarchie [`UpnpObject`] et peut être implémenté
|
||||
/// séparément.
|
||||
pub trait UpnpDeepClone {
|
||||
/// Crée un clone profond de l'objet.
|
||||
///
|
||||
/// Tous les éléments internes sont clonés, créant un objet complètement indépendant.
|
||||
fn deep_clone(&self) -> Self;
|
||||
}
|
||||
|
||||
/// Trait de base pour tous les objets UPnP.
|
||||
///
|
||||
/// Ce trait fournit les fonctionnalités communes à tous les objets UPnP :
|
||||
/// - Sérialisation XML
|
||||
/// - Conversion en Markdown
|
||||
/// - Identification du type d'objet (instance ou set)
|
||||
///
|
||||
/// # Traits requis
|
||||
///
|
||||
/// - [`Clone`] : Pour pouvoir dupliquer les objets
|
||||
/// - [`Debug`] : Pour le débogage
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// Ce trait est à la base de toute la hiérarchie UPnP. Voir la documentation du module
|
||||
/// pour le graphe complet.
|
||||
pub trait UpnpObject: Clone + Debug {
|
||||
/// Convertit l'objet en élément XML.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Un [`Element`] xmltree représentant l'objet.
|
||||
async fn to_xml_element(&self) -> Element;
|
||||
|
||||
/// Convertit l'objet en chaîne XML formatée.
|
||||
///
|
||||
/// Génère une représentation XML complète avec en-tête et indentation.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Une chaîne XML formatée avec :
|
||||
/// - En-tête `<?xml version="1.0" encoding="UTF-8"?>`
|
||||
/// - Indentation de 2 espaces
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let xml = my_object.to_xml();
|
||||
/// println!("{}", xml);
|
||||
/// // <?xml version="1.0" encoding="UTF-8"?>
|
||||
/// // <element>
|
||||
/// // <child>value</child>
|
||||
/// // </element>
|
||||
/// ```
|
||||
async fn to_xml(&self) -> String {
|
||||
let elem = self.to_xml_element().await;
|
||||
|
||||
// Configurer l'indentation
|
||||
let config = EmitterConfig::new()
|
||||
.perform_indent(true)
|
||||
.indent_string(" "); // 2 espaces
|
||||
.indent_string(" ");
|
||||
|
||||
// Sérialiser dans un buffer
|
||||
let mut buf = Vec::new();
|
||||
// écrire l'élément
|
||||
elem.write_with_config(&mut buf, config)
|
||||
.expect("Failed to write XML");
|
||||
|
||||
// Préfixer avec l'en-tête XML
|
||||
let mut xml_string = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".to_string();
|
||||
xml_string.push_str(&String::from_utf8(buf).expect("Invalid UTF-8"));
|
||||
|
||||
xml_string
|
||||
}
|
||||
fn to_markdown(&self) -> String {
|
||||
let elem = self.to_xml_element();
|
||||
|
||||
/// Convertit l'objet en représentation Markdown.
|
||||
///
|
||||
/// Génère une vue hiérarchique de la structure XML en format Markdown,
|
||||
/// avec détection automatique des URLs et images.
|
||||
///
|
||||
/// # Fonctionnalités
|
||||
///
|
||||
/// - Les URLs sont converties en liens cliquables
|
||||
/// - Les URLs d'images sont affichées comme images
|
||||
/// - Les attributs sont formatés comme `key=value`
|
||||
/// - Structure hiérarchique avec indentation
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Une chaîne Markdown formatée.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let md = my_object.to_markdown();
|
||||
/// println!("{}", md);
|
||||
/// // # UPnP XML (Markdown view)
|
||||
/// //
|
||||
/// // - **element**
|
||||
/// // - **child**: `value`
|
||||
/// ```
|
||||
async fn to_markdown(&self) -> String {
|
||||
let elem = self.to_xml_element().await;
|
||||
let mut md = String::new();
|
||||
|
||||
fn is_url(s: &str) -> bool {
|
||||
@@ -43,7 +163,7 @@ pub trait UpnpXml {
|
||||
}
|
||||
|
||||
fn format_value(v: &str) -> String {
|
||||
let v = v.trim().to_string(); // <-- clone du texte nettoyé
|
||||
let v = v.trim().to_string();
|
||||
if is_url(&v) {
|
||||
if is_image_url(&v) {
|
||||
format!("[{}]({})<br>", v, v, v)
|
||||
@@ -59,7 +179,6 @@ pub trait UpnpXml {
|
||||
let indent = " ".repeat(depth);
|
||||
md.push_str(&format!("{}- **{}**", indent, elem.name));
|
||||
|
||||
// Attributs
|
||||
if !elem.attributes.is_empty() {
|
||||
let attrs: Vec<String> = elem
|
||||
.attributes
|
||||
@@ -69,14 +188,16 @@ pub trait UpnpXml {
|
||||
md.push_str(&format!(" ({})", attrs.join(", ")));
|
||||
}
|
||||
|
||||
// Texte (utilise get_text() maintenant)
|
||||
if let Some(text) = elem.get_text().map(|s| s.trim().to_string()).filter(|s| !s.is_empty()) {
|
||||
if let Some(text) = elem
|
||||
.get_text()
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
{
|
||||
md.push_str(&format!(": {}", format_value(&text)));
|
||||
}
|
||||
|
||||
md.push('\n');
|
||||
|
||||
// Enfants
|
||||
for child in &elem.children {
|
||||
if let xmltree::XMLNode::Element(child_elem) = child {
|
||||
recurse(child_elem, md, depth + 1);
|
||||
@@ -88,15 +209,558 @@ pub trait UpnpXml {
|
||||
recurse(&elem, &mut md, 0);
|
||||
md
|
||||
}
|
||||
}
|
||||
pub trait UpnpObject: UpnpXml {
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType;
|
||||
|
||||
fn get_name(&self) -> &String {
|
||||
return &self.as_upnp_object_type().name;
|
||||
/// Indique si l'objet est une instance.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `false` par défaut. Surchargé par [`UpnpInstance`] pour retourner `true`.
|
||||
fn is_instance(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Indique si l'objet est une collection (set).
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `false` par défaut. Surchargé par [`UpnpSet`] pour retourner `true`.
|
||||
fn is_set(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait pour les modèles UPnP qui peuvent créer des instances.
|
||||
///
|
||||
/// Un modèle représente la définition ou template d'un objet UPnP, tandis qu'une
|
||||
/// instance est une occurrence concrète de cet objet.
|
||||
///
|
||||
/// # Type associé
|
||||
///
|
||||
/// - [`Instance`](Self::Instance) : Le type d'instance créée par ce modèle
|
||||
///
|
||||
/// # Méthodes
|
||||
///
|
||||
/// - [`create_instance`](Self::create_instance) : Crée une nouvelle instance
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpObject
|
||||
/// └─> UpnpModel
|
||||
/// ```
|
||||
///
|
||||
/// # Relation avec UpnpInstance
|
||||
///
|
||||
/// `UpnpModel` et [`UpnpInstance`] sont liés via leurs types associés :
|
||||
/// - Le modèle spécifie quel type d'instance il crée
|
||||
/// - L'instance spécifie de quel type de modèle elle provient
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct DeviceModel { /* ... */ }
|
||||
/// struct DeviceInstance { /* ... */ }
|
||||
///
|
||||
/// impl UpnpModel for DeviceModel {
|
||||
/// type Instance = DeviceInstance;
|
||||
/// }
|
||||
///
|
||||
/// impl UpnpInstance for DeviceInstance {
|
||||
/// type Model = DeviceModel;
|
||||
///
|
||||
/// fn new(model: &DeviceModel) -> Self {
|
||||
/// // Création de l'instance depuis le modèle
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Utilisation
|
||||
/// let model = DeviceModel::new();
|
||||
/// let instance = model.create_instance(); // Arc<DeviceInstance>
|
||||
/// ```
|
||||
pub trait UpnpModel: UpnpObject {
|
||||
/// Le type d'instance créée par ce modèle.
|
||||
type Instance: UpnpInstance<Model = Self>;
|
||||
|
||||
/// Crée une nouvelle instance à partir de ce modèle.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Un `Arc` contenant la nouvelle instance créée.
|
||||
///
|
||||
/// # Implémentation par défaut
|
||||
///
|
||||
/// Par défaut, appelle [`UpnpInstance::new`] avec une référence vers ce modèle
|
||||
/// et encapsule le résultat dans un `Arc`.
|
||||
fn create_instance(&self) -> Arc<Self::Instance> {
|
||||
Arc::new(Self::Instance::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait pour les instances UPnP concrètes.
|
||||
///
|
||||
/// Une instance représente une occurrence concrète d'un objet UPnP, créée à partir
|
||||
/// d'un modèle ([`UpnpModel`]).
|
||||
///
|
||||
/// # Type associé
|
||||
///
|
||||
/// - [`Model`](Self::Model) : Le type du modèle dont cette instance dérive
|
||||
///
|
||||
/// # Méthodes requises
|
||||
///
|
||||
/// - [`new`](Self::new) : Constructeur créant l'instance depuis un modèle
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpObject
|
||||
/// └─> UpnpInstance
|
||||
/// ```
|
||||
///
|
||||
/// # Relation avec UpnpModel
|
||||
///
|
||||
/// Voir la documentation de [`UpnpModel`] pour comprendre la relation entre
|
||||
/// modèles et instances.
|
||||
pub trait UpnpInstance: UpnpObject {
|
||||
/// Le type du modèle dont cette instance est dérivée.
|
||||
type Model: UpnpModel<Instance = Self>;
|
||||
|
||||
/// Crée une nouvelle instance à partir d'un modèle.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `model` - Référence vers le modèle à partir duquel créer l'instance
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Une nouvelle instance initialisée depuis le modèle.
|
||||
fn new(model: &Self::Model) -> Self;
|
||||
|
||||
/// Indique que cet objet est une instance.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Toujours `true` pour les instances.
|
||||
fn is_instance(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait pour les objets UPnP typés.
|
||||
///
|
||||
/// Ajoute les informations de type et de nom aux objets UPnP.
|
||||
///
|
||||
/// # Méthodes requises
|
||||
///
|
||||
/// - [`as_upnp_object_type`](Self::as_upnp_object_type) : Accès au type de l'objet
|
||||
///
|
||||
/// # Méthodes fournies
|
||||
///
|
||||
/// - [`get_name`](Self::get_name) : Récupère le nom de l'objet
|
||||
/// - [`get_object_type`](Self::get_object_type) : Récupère le type de l'objet
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpObject
|
||||
/// └─> UpnpTyped
|
||||
/// ```
|
||||
pub trait UpnpTyped: UpnpObject {
|
||||
/// Retourne une référence vers le type de l'objet.
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType;
|
||||
|
||||
/// Retourne le nom de l'objet.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Une référence vers le nom de l'objet.
|
||||
fn get_name(&self) -> &String {
|
||||
&self.as_upnp_object_type().name
|
||||
}
|
||||
|
||||
/// Retourne le type de l'objet sous forme de chaîne.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Une référence vers le type de l'objet (ex: "Device", "Service", etc.).
|
||||
fn get_object_type(&self) -> &String {
|
||||
&self.as_upnp_object_type().object_type
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait marqueur pour les objets UPnP typés.
|
||||
///
|
||||
/// Combine [`UpnpObject`] et [`UpnpTyped`] pour créer un objet avec toutes
|
||||
/// les fonctionnalités de base plus les informations de type.
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpObject + UpnpTyped
|
||||
/// └─> UpnpTypedObject
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// C'est un *marker trait* (trait marqueur) sans méthodes supplémentaires.
|
||||
pub trait UpnpTypedObject: UpnpObject + UpnpTyped {}
|
||||
|
||||
/// Trait marqueur pour les instances typées UPnP.
|
||||
///
|
||||
/// Combine [`UpnpTypedObject`] et [`UpnpInstance`] pour représenter une instance
|
||||
/// concrète d'un objet typé avec toutes les fonctionnalités :
|
||||
/// - Sérialisation XML/Markdown (de [`UpnpObject`])
|
||||
/// - Informations de type et nom (de [`UpnpTyped`])
|
||||
/// - Relation avec un modèle (de [`UpnpInstance`])
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpTypedObject + UpnpInstance
|
||||
/// └─> UpnpTypedInstance
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Ce trait ajoute la méthode [`get_model`](Self::get_model) pour accéder
|
||||
/// au modèle de l'instance. Les collections d'instances ([`UpnInstanceSet`])
|
||||
/// n'ont pas cette méthode car elles contiennent plusieurs instances.
|
||||
pub trait UpnpTypedInstance: UpnpTypedObject + UpnpInstance
|
||||
where
|
||||
Self::Model: UpnpModel<Instance = Self>
|
||||
{
|
||||
/// Retourne une référence vers le modèle dont cette instance est dérivée.
|
||||
///
|
||||
/// Permet d'accéder aux métadonnées et contraintes définies dans le modèle,
|
||||
/// telles que les plages de valeurs autorisées, les types, les descriptions, etc.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Une référence immuable vers le modèle.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let instance = model.create_instance();
|
||||
///
|
||||
/// // Accéder aux propriétés du modèle depuis l'instance
|
||||
/// let model_ref = instance.get_model();
|
||||
/// println!("Instance du modèle: {}", model_ref.get_name());
|
||||
///
|
||||
/// // Vérifier les contraintes définies dans le modèle
|
||||
/// if let Some(range) = model_ref.get_range() {
|
||||
/// println!("Plage autorisée: {:?}", range);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Use cases
|
||||
///
|
||||
/// Cette méthode est particulièrement utile pour :
|
||||
/// - Valider des valeurs contre les contraintes du modèle
|
||||
/// - Accéder aux métadonnées sans dupliquer les informations
|
||||
/// - Afficher des informations de type ou de description
|
||||
/// - Implémenter des logiques conditionnelles basées sur le modèle
|
||||
///
|
||||
/// # Différence avec les traits spécifiques
|
||||
///
|
||||
/// Pour les variables d'état, le trait [`UpnpVariable`](crate::state_variables::UpnpVariable)
|
||||
/// fournit également `get_definition()` qui est sémantiquement équivalent
|
||||
/// mais spécifique au domaine des variables.
|
||||
fn get_model(&self) -> &Self::Model;
|
||||
}
|
||||
|
||||
|
||||
/// Trait marqueur pour les collections UPnP.
|
||||
///
|
||||
/// Représente un ensemble (set) d'objets UPnP.
|
||||
///
|
||||
/// # Super-traits requis
|
||||
///
|
||||
/// - [`UpnpObject`] : Fonctionnalités de base (XML, etc.)
|
||||
/// - [`UpnpDeepClone`] : Permet le clonage profond des collections
|
||||
///
|
||||
/// # Implémentation
|
||||
///
|
||||
/// Ce trait surcharge [`UpnpObject::is_set`] pour retourner `true`.
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpObject + UpnpDeepClone
|
||||
/// └─> UpnpSet
|
||||
/// ```
|
||||
///
|
||||
/// # Note sur le clonage
|
||||
///
|
||||
/// Les collections UPnP contiennent généralement des `Arc<T>` vers leurs éléments.
|
||||
/// Le trait [`Clone`] (via `UpnpObject`) effectue un clonage shallow des `Arc`,
|
||||
/// tandis que [`UpnpDeepClone`] clone profondément les éléments contenus.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct ServiceSet {
|
||||
/// services: HashMap<String, Arc<Service>>,
|
||||
/// }
|
||||
///
|
||||
/// impl Clone for ServiceSet {
|
||||
/// fn clone(&self) -> Self {
|
||||
/// // Clone shallow : partage les Services via Arc
|
||||
/// Self {
|
||||
/// services: self.services.clone()
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl UpnpDeepClone for ServiceSet {
|
||||
/// fn deep_clone(&self) -> Self {
|
||||
/// // Clone profond : crée de nouveaux Services
|
||||
/// let deep_services = self.services
|
||||
/// .iter()
|
||||
/// .map(|(k, v)| (k.clone(), Arc::new((**v).clone())))
|
||||
/// .collect();
|
||||
///
|
||||
/// Self {
|
||||
/// services: deep_services
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait UpnpSet: UpnpObject + UpnpDeepClone {
|
||||
/// Indique que cet objet est une collection.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Toujours `true` pour les collections.
|
||||
fn is_set(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait marqueur pour les collections de modèles UPnP.
|
||||
///
|
||||
/// Combine [`UpnpSet`] et [`UpnpModel`] pour représenter une collection
|
||||
/// de modèles qui peut elle-même créer une collection d'instances.
|
||||
///
|
||||
/// # Cas d'usage
|
||||
///
|
||||
/// Ce trait est utilisé quand une collection de modèles doit pouvoir instancier
|
||||
/// une collection d'instances correspondante. Par exemple :
|
||||
/// - Un ensemble de modèles de services d'un device qui crée un ensemble d'instances de services
|
||||
/// - Une liste de modèles d'actions qui instancie une liste d'actions actives
|
||||
/// - Une collection de modèles de variables d'état qui génère une collection d'instances
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpSet + UpnpModel
|
||||
/// └─> UpnpModelSet
|
||||
/// ```
|
||||
///
|
||||
/// # Relation avec d'autres traits
|
||||
///
|
||||
/// - [`UpnpSet`] : Fournit les fonctionnalités de collection
|
||||
/// - [`UpnpModel`] : Fournit la capacité de créer des instances
|
||||
/// - [`UpnInstanceSet`] : Représente les collections d'instances (contrepartie)
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// C'est un *marker trait* (trait marqueur) sans méthodes supplémentaires.
|
||||
/// Il est automatiquement implémenté pour tous les types éligibles via une
|
||||
/// blanket implementation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// /// Collection de modèles de services
|
||||
/// struct ServiceSetModel {
|
||||
/// services: Vec<Arc<ServiceModel>>,
|
||||
/// }
|
||||
///
|
||||
/// /// Collection d'instances de services
|
||||
/// struct ServiceSetInstance {
|
||||
/// model: Arc<ServiceSetModel>,
|
||||
/// service_instances: Vec<Arc<ServiceInstance>>,
|
||||
/// }
|
||||
///
|
||||
/// impl UpnpObject for ServiceSetModel { /* ... */ }
|
||||
/// impl UpnpSet for ServiceSetModel {}
|
||||
///
|
||||
/// impl UpnpModel for ServiceSetModel {
|
||||
/// type Instance = ServiceSetInstance;
|
||||
///
|
||||
/// fn create_instance(&self) -> Arc<ServiceSetInstance> {
|
||||
/// // Créer des instances pour chaque service
|
||||
/// let instances = self.services
|
||||
/// .iter()
|
||||
/// .map(|model| model.create_instance())
|
||||
/// .collect();
|
||||
///
|
||||
/// Arc::new(ServiceSetInstance {
|
||||
/// model: Arc::new(self.clone()),
|
||||
/// service_instances: instances,
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // UpnpModelSet est automatiquement implémenté !
|
||||
///
|
||||
/// // Utilisation
|
||||
/// let model_set = ServiceSetModel::new();
|
||||
/// let instance_set = model_set.create_instance(); // Crée toutes les instances
|
||||
/// ```
|
||||
pub trait UpnpModelSet: UpnpSet + UpnpModel {}
|
||||
|
||||
|
||||
/// Trait marqueur pour les collections d'instances UPnP.
|
||||
///
|
||||
/// Combine [`UpnpSet`] et [`UpnpInstance`] pour représenter une collection
|
||||
/// d'instances UPnP. Cela permet d'avoir des collections qui sont elles-mêmes
|
||||
/// des instances créées depuis un modèle.
|
||||
///
|
||||
/// # Hiérarchie
|
||||
///
|
||||
/// ```text
|
||||
/// UpnpSet + UpnpInstance
|
||||
/// └─> UpnInstanceSet
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// C'est un *marker trait* (trait marqueur) sans méthodes supplémentaires.
|
||||
pub trait UpnInstanceSet: UpnpSet + UpnpInstance {}
|
||||
|
||||
|
||||
/// Implémentation automatique de [`UpnInstanceSet`] pour tous les types éligibles.
|
||||
///
|
||||
/// Cette *blanket implementation* fournit automatiquement le trait [`UpnInstanceSet`]
|
||||
/// à tout type `T` qui implémente à la fois [`UpnpSet`] et [`UpnpInstance`].
|
||||
///
|
||||
/// # Contraintes
|
||||
///
|
||||
/// - `T` doit implémenter [`UpnpSet`] (collection d'objets UPnP)
|
||||
/// - `T` doit implémenter [`UpnpInstance`] (instance créée depuis un modèle)
|
||||
///
|
||||
/// # Pourquoi cette implémentation existe
|
||||
///
|
||||
/// Certaines collections UPnP sont elles-mêmes des instances (par exemple, une
|
||||
/// collection de services pour un device spécifique). Ce trait marker permet
|
||||
/// d'identifier ces collections qui combinent les deux aspects. La blanket
|
||||
/// implementation évite d'avoir à l'implémenter manuellement pour chaque type.
|
||||
///
|
||||
/// # Utilisation
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct ServiceSetInstance {
|
||||
/// model: Arc<ServiceSetModel>,
|
||||
/// services: Vec<Arc<ServiceInstance>>,
|
||||
/// }
|
||||
///
|
||||
/// impl UpnpObject for ServiceSetInstance { /* ... */ }
|
||||
/// impl UpnpSet for ServiceSetInstance {}
|
||||
/// impl UpnpInstance for ServiceSetInstance {
|
||||
/// type Model = ServiceSetModel;
|
||||
/// fn new(model: &ServiceSetModel) -> Self { /* ... */ }
|
||||
/// }
|
||||
///
|
||||
/// // UpnInstanceSet est automatiquement implémenté !
|
||||
///
|
||||
/// fn process_instance_set<T: UpnInstanceSet>(set: &T) {
|
||||
/// if set.is_set() && set.is_instance() {
|
||||
/// println!("C'est une collection ET une instance");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
impl<T> UpnInstanceSet for T
|
||||
where
|
||||
T: UpnpSet + UpnpInstance
|
||||
{}
|
||||
|
||||
/// Implémentation automatique de [`UpnpTypedObject`] pour tous les types éligibles.
|
||||
///
|
||||
/// Cette *blanket implementation* fournit automatiquement le trait [`UpnpTypedObject`]
|
||||
/// à tout type `T` qui implémente à la fois [`UpnpObject`] et [`UpnpTyped`].
|
||||
///
|
||||
/// # Contraintes
|
||||
///
|
||||
/// - `T` doit implémenter [`UpnpObject`] (fonctionnalités de base UPnP)
|
||||
/// - `T` doit implémenter [`UpnpTyped`] (informations de type et nom)
|
||||
///
|
||||
/// # Utilisation
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct Device {
|
||||
/// object_type: UpnpObjectType,
|
||||
/// }
|
||||
///
|
||||
/// impl UpnpObject for Device { /* ... */ }
|
||||
/// impl UpnpTyped for Device { /* ... */ }
|
||||
///
|
||||
/// // UpnpTypedObject est automatiquement implémenté !
|
||||
/// fn process<T: UpnpTypedObject>(obj: &T) {
|
||||
/// println!("{}", obj.get_name());
|
||||
/// }
|
||||
/// ```
|
||||
impl<T> UpnpTypedObject for T
|
||||
where
|
||||
T: UpnpObject + UpnpTyped
|
||||
{}
|
||||
|
||||
|
||||
/// Implémentation automatique de [`UpnpModelSet`] pour tous les types éligibles.
|
||||
///
|
||||
/// Cette *blanket implementation* fournit automatiquement le trait [`UpnpModelSet`]
|
||||
/// à tout type `T` qui implémente à la fois [`UpnpSet`] et [`UpnpModel`].
|
||||
///
|
||||
/// # Contraintes
|
||||
///
|
||||
/// - `T` doit implémenter [`UpnpSet`] (collection d'objets UPnP)
|
||||
/// - `T` doit implémenter [`UpnpModel`] (peut créer des instances)
|
||||
///
|
||||
/// # Pourquoi cette implémentation existe
|
||||
///
|
||||
/// [`UpnpModelSet`] est un *marker trait* qui identifie les collections pouvant
|
||||
/// créer des collections d'instances. Plutôt que de demander aux développeurs
|
||||
/// d'écrire manuellement `impl UpnpModelSet for MyType {}`, cette blanket
|
||||
/// implementation le fait automatiquement dès que les traits requis sont implémentés.
|
||||
///
|
||||
/// # Fonctionnement
|
||||
///
|
||||
/// Lorsque vous définissez une collection de modèles :
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct ActionSetModel {
|
||||
/// actions: Vec<Arc<ActionModel>>,
|
||||
/// }
|
||||
///
|
||||
/// impl UpnpObject for ActionSetModel { /* ... */ }
|
||||
/// impl UpnpSet for ActionSetModel {}
|
||||
///
|
||||
/// impl UpnpModel for ActionSetModel {
|
||||
/// type Instance = ActionSetInstance;
|
||||
/// fn create_instance(&self) -> Arc<ActionSetInstance> { /* ... */ }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Le compilateur Rust vérifie automatiquement que `ActionSetModel` satisfait
|
||||
/// toutes les contraintes (implémente `UpnpSet` ET `UpnpModel`) et applique
|
||||
/// donc `UpnpModelSet` sans code supplémentaire.
|
||||
///
|
||||
/// # Utilisation dans des signatures génériques
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn process_model_set<T: UpnpModelSet>(set: &T) {
|
||||
/// println!("Processing model set that can create instances");
|
||||
/// let instance = set.create_instance();
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Différence avec UpnInstanceSet
|
||||
///
|
||||
/// - [`UpnpModelSet`] : Collection de **modèles** (peut créer des instances)
|
||||
/// - [`UpnInstanceSet`] : Collection d'**instances** (créée depuis un modèle)
|
||||
impl<T> UpnpModelSet for T
|
||||
where
|
||||
T: UpnpSet + UpnpModel
|
||||
{}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user