virustotal_rs/iterator_utils/
core.rs

1//! Core iterator implementations
2
3use super::traits::PaginatedIterator;
4use crate::client_utils::RateLimiter;
5use crate::{Client, Error, Result};
6use serde::de::DeserializeOwned;
7use std::marker::PhantomData;
8use std::sync::Arc;
9
10/// Configuration for enhanced collection iterator
11#[derive(Clone, Default)]
12pub struct IteratorConfig {
13    pub cursor: Option<String>,
14    pub limit: Option<u32>,
15    pub rate_limiter: Option<Arc<crate::client_utils::TokenBucketLimiter>>,
16}
17
18impl std::fmt::Debug for IteratorConfig {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        f.debug_struct("IteratorConfig")
21            .field("cursor", &self.cursor)
22            .field("limit", &self.limit)
23            .field(
24                "rate_limiter",
25                &self.rate_limiter.as_ref().map(|_| "TokenBucketLimiter"),
26            )
27            .finish()
28    }
29}
30
31/// Enhanced collection iterator with rate limiting and progress tracking
32pub struct EnhancedCollectionIterator<'a, T> {
33    client: &'a Client,
34    url: String,
35    config: IteratorConfig,
36    finished: bool,
37    total_fetched: u64,
38    batch_count: u32,
39    _phantom: PhantomData<T>,
40}
41
42/// Constructor methods for EnhancedCollectionIterator
43impl<'a, T> EnhancedCollectionIterator<'a, T>
44where
45    T: DeserializeOwned + Clone,
46{
47    /// Create a new enhanced iterator with default configuration
48    pub fn new(client: &'a Client, url: impl Into<String>) -> Self {
49        Self::with_config(client, url, IteratorConfig::default())
50    }
51
52    /// Create a new enhanced iterator with custom configuration
53    pub fn with_config(client: &'a Client, url: impl Into<String>, config: IteratorConfig) -> Self {
54        Self {
55            client,
56            url: url.into(),
57            config,
58            finished: false,
59            total_fetched: 0,
60            batch_count: 0,
61            _phantom: PhantomData,
62        }
63    }
64}
65
66/// Builder pattern methods for EnhancedCollectionIterator
67impl<'a, T> EnhancedCollectionIterator<'a, T>
68where
69    T: DeserializeOwned + Clone,
70{
71    /// Set batch size limit
72    pub fn with_limit(mut self, limit: u32) -> Self {
73        self.config.limit = Some(limit);
74        self
75    }
76
77    /// Set custom rate limiter
78    pub fn with_rate_limiter(
79        mut self,
80        limiter: Arc<crate::client_utils::TokenBucketLimiter>,
81    ) -> Self {
82        self.config.rate_limiter = Some(limiter);
83        self
84    }
85}
86
87/// Accessor methods for EnhancedCollectionIterator
88impl<'a, T> EnhancedCollectionIterator<'a, T>
89where
90    T: DeserializeOwned + Clone,
91{
92    /// Get total items fetched so far
93    pub fn total_fetched(&self) -> u64 {
94        self.total_fetched
95    }
96
97    /// Get number of batches fetched
98    pub fn batch_count(&self) -> u32 {
99        self.batch_count
100    }
101}
102
103/// Internal utility methods for EnhancedCollectionIterator
104impl<'a, T> EnhancedCollectionIterator<'a, T>
105where
106    T: DeserializeOwned + Clone,
107{
108    /// Build the API URL with query parameters
109    fn build_url(&self) -> String {
110        let mut url = self.url.clone();
111        let mut query_params = Vec::new();
112
113        if let Some(cursor) = &self.config.cursor {
114            query_params.push(format!("cursor={}", cursor));
115        }
116
117        if let Some(limit) = self.config.limit {
118            query_params.push(format!("limit={}", limit));
119        }
120
121        if !query_params.is_empty() {
122            url = format!("{}?{}", url, query_params.join("&"));
123        }
124
125        url
126    }
127
128    /// Process API response and update iterator state
129    fn process_response(&mut self, response: crate::objects::Collection<T>) -> Vec<T> {
130        let items = response.data;
131        self.total_fetched += items.len() as u64;
132        self.batch_count += 1;
133
134        if let Some(meta) = response.meta {
135            self.config.cursor = meta.cursor;
136            if self.config.cursor.is_none() {
137                self.finished = true;
138            }
139        } else {
140            self.finished = true;
141        }
142
143        items
144    }
145}
146
147#[async_trait::async_trait]
148impl<'a, T> PaginatedIterator<T> for EnhancedCollectionIterator<'a, T>
149where
150    T: DeserializeOwned + Clone + Send + Sync,
151{
152    type Error = Error;
153
154    async fn next_batch(&mut self) -> Result<Vec<T>> {
155        if self.finished {
156            return Ok(Vec::new());
157        }
158
159        // Apply rate limiting if configured
160        if let Some(ref limiter) = self.config.rate_limiter {
161            limiter.check_rate_limit().await?;
162        }
163
164        let url = self.build_url();
165        let response: crate::objects::Collection<T> = self.client.get(&url).await?;
166        let items = self.process_response(response);
167
168        Ok(items)
169    }
170
171    fn has_more(&self) -> bool {
172        !self.finished
173    }
174
175    fn hint_remaining(&self) -> Option<usize> {
176        // We don't have enough info to estimate remaining items
177        None
178    }
179
180    fn stats(&self) -> super::traits::IteratorStats {
181        super::traits::IteratorStats {
182            batches_fetched: self.batch_count as u64,
183            items_fetched: self.total_fetched,
184            ..Default::default()
185        }
186    }
187}
188
189// Note: Extension methods for CollectionIterator would need public fields
190// or accessor methods. For now, use EnhancedCollectionIterator directly.
191
192/// Adapter to convert CollectionIterator to PaginatedIterator
193pub struct CollectionIteratorAdapter<'a, T> {
194    inner: crate::objects::CollectionIterator<'a, T>,
195}
196
197#[async_trait::async_trait]
198impl<'a, T> PaginatedIterator<T> for CollectionIteratorAdapter<'a, T>
199where
200    T: DeserializeOwned + Clone + Send + Sync,
201{
202    type Error = Error;
203
204    async fn next_batch(&mut self) -> Result<Vec<T>> {
205        if self.inner.is_finished() {
206            return Ok(Vec::new());
207        }
208
209        self.inner.next_batch().await
210    }
211
212    fn has_more(&self) -> bool {
213        !self.inner.is_finished()
214    }
215}
216
217impl<'a, T> From<crate::objects::CollectionIterator<'a, T>> for CollectionIteratorAdapter<'a, T> {
218    fn from(inner: crate::objects::CollectionIterator<'a, T>) -> Self {
219        Self { inner }
220    }
221}