objets_metier_rs 1.0.2

Bibliothèque Rust moderne et sûre pour l'API COM Objets Métier Sage 100c - Production Ready
use super::{SafeDispatch, SafeVariant};
use crate::errors::{SageError, SageResult};
use windows::Win32::System::Com::IDispatch;

/// Collection COM générique pour itérer sur les objets Sage
///
/// Wrapper pour les collections COM Sage qui implémente le pattern standard:
/// - `Count` property pour le nombre d'éléments
/// - `Item(index)` method pour accéder aux éléments (index 1-based)
///
/// # Exemple
/// ```ignore
/// // Récupérer une collection depuis une factory
/// let collection_variant = factory.list()?;
/// let collection = ComCollection::from_variant(collection_variant)?;
///
/// // Itérer sur les éléments
/// for item in collection.iter() {
///     let dispatch = item?;
///     // Utiliser le dispatch...
/// }
/// ```
pub struct ComCollection {
    dispatch: IDispatch,
}

impl ComCollection {
    /// Crée une collection à partir d'un IDispatch
    pub fn new(dispatch: IDispatch) -> Self {
        Self { dispatch }
    }

    /// Crée une collection à partir d'un SafeVariant
    /// Le variant doit contenir un objet IDispatch
    pub fn from_variant(variant: SafeVariant) -> SageResult<Self> {
        if variant.is_object() {
            let dispatch = variant.to_dispatch()?;
            Ok(Self::new(dispatch))
        } else {
            Err(SageError::ConversionError {
                from_type: "SafeVariant".to_string(),
                to_type: "ComCollection".to_string(),
                value: "Le variant ne contient pas un objet COM".to_string(),
            })
        }
    }

    /// Crée un SafeDispatch temporaire pour les appels
    fn dispatch(&self) -> SafeDispatch<'_> {
        SafeDispatch::new(&self.dispatch)
    }

    /// Nombre d'éléments dans la collection
    pub fn count(&self) -> SageResult<i32> {
        self.dispatch().get_property_by_name("Count")?.to_i32()
    }

    /// Récupère un élément par index (1-based comme en VB/COM)
    ///
    /// # Arguments
    /// * `index` - Index de l'élément (commence à 1)
    ///
    /// # Returns
    /// L'IDispatch de l'élément à l'index spécifié
    pub fn item(&self, index: i32) -> SageResult<IDispatch> {
        let index_variant = SafeVariant::I4(index);
        let result = self
            .dispatch()
            .call_method_by_name("Item", &[index_variant])?;

        result.to_dispatch()
    }

    /// Vérifie si la collection est vide
    pub fn is_empty(&self) -> SageResult<bool> {
        Ok(self.count()? == 0)
    }

    /// Retourne un itérateur sur les éléments de la collection
    pub fn iter(&self) -> ComCollectionIterator<'_> {
        ComCollectionIterator {
            collection: self,
            current_index: 1,
            count: self.count().unwrap_or(0),
        }
    }

    /// Collecte tous les éléments en un Vec<IDispatch>
    pub fn to_vec(&self) -> SageResult<Vec<IDispatch>> {
        let count = self.count()?;
        let mut items = Vec::with_capacity(count as usize);

        for i in 1..=count {
            items.push(self.item(i)?);
        }

        Ok(items)
    }
}

/// Itérateur pour parcourir une ComCollection
pub struct ComCollectionIterator<'a> {
    collection: &'a ComCollection,
    current_index: i32,
    count: i32,
}

impl<'a> Iterator for ComCollectionIterator<'a> {
    type Item = SageResult<IDispatch>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_index > self.count {
            return None;
        }

        let result = self.collection.item(self.current_index);
        self.current_index += 1;
        Some(result)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let remaining = (self.count - self.current_index + 1) as usize;
        (remaining, Some(remaining))
    }
}

impl<'a> ExactSizeIterator for ComCollectionIterator<'a> {}

/// Collection typée générique pour itérer sur des objets spécifiques
///
/// Cette structure permet de créer des collections typées qui retournent
/// directement le type d'objet souhaité au lieu de IDispatch.
///
/// # Exemple
/// ```ignore
/// // Créer une collection typée pour des Journaux
/// let typed_collection = TypedComCollection::<Journal>::new(collection);
///
/// // Itérer sur les journaux
/// for journal_result in typed_collection.iter() {
///     let journal = journal_result?;
///     println!("Journal: {}", journal.jo_intitule()?);
/// }
/// ```
pub struct TypedComCollection<T>
where
    T: FromDispatchNew,
{
    inner: ComCollection,
    _marker: std::marker::PhantomData<T>,
}

impl<T> TypedComCollection<T>
where
    T: FromDispatchNew,
{
    /// Crée une collection typée à partir d'une ComCollection
    pub fn new(collection: ComCollection) -> Self {
        Self {
            inner: collection,
            _marker: std::marker::PhantomData,
        }
    }

    /// Crée une collection typée à partir d'un SafeVariant
    pub fn from_variant(variant: SafeVariant) -> SageResult<Self> {
        let collection = ComCollection::from_variant(variant)?;
        Ok(Self::new(collection))
    }

    /// Nombre d'éléments dans la collection
    pub fn count(&self) -> SageResult<i32> {
        self.inner.count()
    }

    /// Vérifie si la collection est vide
    pub fn is_empty(&self) -> SageResult<bool> {
        self.inner.is_empty()
    }

    /// Récupère un élément typé par index (1-based)
    pub fn item(&self, index: i32) -> SageResult<T> {
        let dispatch = self.inner.item(index)?;
        T::from_dispatch_new(dispatch)
    }

    /// Retourne un itérateur sur les éléments typés
    pub fn iter(&self) -> TypedComCollectionIterator<'_, T> {
        TypedComCollectionIterator {
            collection: self,
            current_index: 1,
            count: self.inner.count().unwrap_or(0),
        }
    }

    /// Collecte tous les éléments en un Vec<T>
    pub fn to_vec(&self) -> SageResult<Vec<T>> {
        let count = self.inner.count()?;
        let mut items = Vec::with_capacity(count as usize);

        for i in 1..=count {
            items.push(self.item(i)?);
        }

        Ok(items)
    }
}

/// Itérateur typé pour parcourir une TypedComCollection
pub struct TypedComCollectionIterator<'a, T>
where
    T: FromDispatchNew,
{
    collection: &'a TypedComCollection<T>,
    current_index: i32,
    count: i32,
}

impl<'a, T> Iterator for TypedComCollectionIterator<'a, T>
where
    T: FromDispatchNew,
{
    type Item = SageResult<T>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_index > self.count {
            return None;
        }

        let result = self.collection.item(self.current_index);
        self.current_index += 1;
        Some(result)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let remaining = (self.count - self.current_index + 1) as usize;
        (remaining, Some(remaining))
    }
}

impl<'a, T> ExactSizeIterator for TypedComCollectionIterator<'a, T> where T: FromDispatchNew {}

/// Trait pour créer un wrapper depuis un IDispatch
///
/// Ce trait doit être implémenté par tous les wrappers d'objets Sage
/// pour pouvoir être utilisés avec TypedComCollection.
pub trait FromDispatchNew: Sized {
    /// Crée une instance du wrapper à partir d'un IDispatch
    fn from_dispatch_new(dispatch: IDispatch) -> SageResult<Self>;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_collection_documentation() {
        // Test de documentation - les collections COM nécessitent un environnement Sage

        // Utilisation typique avec une factory:
        // let result = factory.list()?;
        // let collection = ComCollection::from_variant(result)?;
        //
        // println!("Nombre d'éléments: {}", collection.count()?);
        //
        // for item in collection.iter() {
        //     let dispatch = item?;
        //     // Traiter l'élément...
        // }

        // Ou avec une collection typée:
        // let typed = TypedComCollection::<Journal>::from_variant(result)?;
        // for journal in typed.iter() {
        //     let j = journal?;
        //     println!("{}", j.jo_intitule()?);
        // }
    }
}