virustotal_rs/iterator_utils/
core.rs1use 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#[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
31pub 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
42impl<'a, T> EnhancedCollectionIterator<'a, T>
44where
45 T: DeserializeOwned + Clone,
46{
47 pub fn new(client: &'a Client, url: impl Into<String>) -> Self {
49 Self::with_config(client, url, IteratorConfig::default())
50 }
51
52 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
66impl<'a, T> EnhancedCollectionIterator<'a, T>
68where
69 T: DeserializeOwned + Clone,
70{
71 pub fn with_limit(mut self, limit: u32) -> Self {
73 self.config.limit = Some(limit);
74 self
75 }
76
77 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
87impl<'a, T> EnhancedCollectionIterator<'a, T>
89where
90 T: DeserializeOwned + Clone,
91{
92 pub fn total_fetched(&self) -> u64 {
94 self.total_fetched
95 }
96
97 pub fn batch_count(&self) -> u32 {
99 self.batch_count
100 }
101}
102
103impl<'a, T> EnhancedCollectionIterator<'a, T>
105where
106 T: DeserializeOwned + Clone,
107{
108 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 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 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 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
189pub 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}