Skip to main content

cassandra_cpp/cassandra/
iterator.rs

1use crate::cassandra::error::*;
2use crate::cassandra::field::Field;
3use crate::cassandra::schema::aggregate_meta::AggregateMeta;
4use crate::cassandra::schema::column_meta::ColumnMeta;
5use crate::cassandra::schema::function_meta::FunctionMeta;
6use crate::cassandra::schema::keyspace_meta::KeyspaceMeta;
7use crate::cassandra::schema::table_meta::TableMeta;
8use crate::cassandra::util::{Protected, ProtectedInner};
9use crate::cassandra::value::Value;
10
11// use cassandra_sys::CassIteratorType as _CassIteratorType;
12use crate::cassandra_sys::cass_false;
13use crate::cassandra_sys::CassIterator as _CassIterator;
14// use cassandra_sys::cass_iterator_type;
15use crate::cassandra_sys::cass_iterator_free;
16use crate::cassandra_sys::cass_iterator_get_aggregate_meta;
17use crate::cassandra_sys::cass_iterator_get_column_meta;
18use crate::cassandra_sys::cass_iterator_get_function_meta;
19use crate::cassandra_sys::cass_iterator_get_keyspace_meta;
20use crate::cassandra_sys::cass_iterator_get_map_key;
21use crate::cassandra_sys::cass_iterator_get_map_value;
22use crate::cassandra_sys::cass_iterator_get_meta_field_name;
23use crate::cassandra_sys::cass_iterator_get_meta_field_value;
24use crate::cassandra_sys::cass_iterator_get_table_meta;
25use crate::cassandra_sys::cass_iterator_get_user_type_field_name;
26use crate::cassandra_sys::cass_iterator_get_user_type_field_value;
27use crate::cassandra_sys::cass_iterator_get_value;
28use crate::cassandra_sys::cass_iterator_next;
29use crate::cassandra_sys::cass_true;
30use crate::cassandra_sys::CassKeyspaceMeta;
31use crate::cassandra_sys::CassSchemaMeta;
32use crate::cassandra_sys::CassTableMeta;
33use crate::cassandra_sys::CassValue as _CassValue;
34
35use std::marker::PhantomData;
36use std::{slice, str};
37
38/// Iterator that only allows access to a single item at a time. You must stop
39/// using the returned item before getting the next.
40///
41/// Ultimately we will move to use a common crate for this, but to date
42/// there is no good crate to follow.
43/// https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#generic-associated-types-gats
44/// and https://github.com/Crazytieguy/gat-lending-iterator were references
45/// for this code.
46///
47/// The idiomatic way to work with this trait is as follows:
48///
49/// ```
50/// # use cassandra_cpp::*;
51/// # struct MyLI;
52/// # impl LendingIterator for MyLI {
53/// #   type Item<'a> = ();
54/// #   fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> { None }
55/// #   fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(0)) }
56/// # }
57/// # let mut lending_iterator = MyLI;
58/// while let Some(row) = lending_iterator.next() {
59///   // ... do something with `row` ...
60/// }
61/// ```
62pub trait LendingIterator {
63    /// The type of each item.
64    type Item<'a>
65    where
66        Self: 'a;
67
68    /// Retrieve the next item from the iterator; it lives only as long as the
69    /// mutable reference to the iterator.
70    fn next(&mut self) -> Option<Self::Item<'_>>;
71
72    /// Minimum and optional maximum expected length of the iterator.
73    /// Default implementation returns `(0, None)`.
74    fn size_hint(&self) -> (usize, Option<usize>) {
75        (0, None)
76    }
77}
78
79/// Iterator over the aggregates in the keyspace.
80///
81/// A Cassandra iterator is a `LendingIterator` because it borrows from some
82/// underlying value, but owns a single item. Each time `next()` is invoked it
83/// decodes the current item into that item, thus invalidating its previous
84/// value.
85#[derive(Debug)]
86pub struct AggregateIterator<'a>(*mut _CassIterator, PhantomData<&'a CassKeyspaceMeta>);
87
88// The underlying C type has no thread-local state, and forbids only concurrent
89// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
90unsafe impl Send for AggregateIterator<'_> {}
91unsafe impl Sync for AggregateIterator<'_> {}
92
93impl Drop for AggregateIterator<'_> {
94    fn drop(&mut self) {
95        unsafe { cass_iterator_free(self.0) }
96    }
97}
98
99impl LendingIterator for AggregateIterator<'_> {
100    type Item<'a>
101        = AggregateMeta<'a>
102    where
103        Self: 'a;
104    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
105        unsafe {
106            match cass_iterator_next(self.0) {
107                cass_false => None,
108                cass_true => {
109                    let field_value = cass_iterator_get_aggregate_meta(self.0);
110                    Some(AggregateMeta::build(field_value))
111                }
112            }
113        }
114    }
115}
116
117/// Iterator over the fields of a UDT
118///
119/// A Cassandra iterator is a `LendingIterator` because it borrows from some
120/// underlying value, but owns a single item. Each time `next()` is invoked it
121/// decodes the current item into that item, thus invalidating its previous
122/// value.
123#[derive(Debug)]
124pub struct UserTypeIterator<'a>(*mut _CassIterator, PhantomData<&'a _CassValue>);
125
126// The underlying C type has no thread-local state, and forbids only concurrent
127// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
128unsafe impl Send for UserTypeIterator<'_> {}
129unsafe impl Sync for UserTypeIterator<'_> {}
130
131impl Drop for UserTypeIterator<'_> {
132    fn drop(&mut self) {
133        unsafe { cass_iterator_free(self.0) }
134    }
135}
136
137impl LendingIterator for UserTypeIterator<'_> {
138    type Item<'a>
139        = (String, Value<'a>)
140    where
141        Self: 'a;
142    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
143        unsafe {
144            match cass_iterator_next(self.0) {
145                cass_false => None,
146                cass_true => Some((self.get_field_name(), self.get_field_value())),
147            }
148        }
149    }
150}
151
152impl UserTypeIterator<'_> {
153    fn get_field_name(&self) -> String {
154        unsafe {
155            let mut name = std::ptr::null();
156            let mut name_length = 0;
157            cass_iterator_get_user_type_field_name(self.0, &mut name, &mut name_length)
158                .to_result(())
159                .and_then(|_| {
160                    let slice = slice::from_raw_parts(name as *const u8, name_length);
161                    let name = str::from_utf8(slice)?.to_owned();
162                    Ok(name)
163                })
164                .expect("Cassandra error during iteration")
165        }
166    }
167
168    fn get_field_value(&self) -> Value<'_> {
169        unsafe { Value::build(cass_iterator_get_user_type_field_value(self.0)) }
170    }
171}
172
173/// Iterator over the functions in a keyspace.
174///
175/// A Cassandra iterator is a `LendingIterator` because it borrows from some
176/// underlying value, but owns a single item. Each time `next()` is invoked it
177/// decodes the current item into that item, thus invalidating its previous
178/// value.
179#[derive(Debug)]
180pub struct FunctionIterator<'a>(*mut _CassIterator, PhantomData<&'a CassKeyspaceMeta>);
181
182// The underlying C type has no thread-local state, and forbids only concurrent
183// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
184unsafe impl Send for FunctionIterator<'_> {}
185unsafe impl Sync for FunctionIterator<'_> {}
186
187impl LendingIterator for FunctionIterator<'_> {
188    type Item<'a>
189        = FunctionMeta<'a>
190    where
191        Self: 'a;
192    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
193        unsafe {
194            match cass_iterator_next(self.0) {
195                cass_false => None,
196                cass_true => Some(FunctionMeta::build(cass_iterator_get_function_meta(self.0))),
197            }
198        }
199    }
200}
201
202/// Iterator over the tables in a keyspace.
203///
204/// A Cassandra iterator is a `LendingIterator` because it borrows from some
205/// underlying value, but owns a single item. Each time `next()` is invoked it
206/// decodes the current item into that item, thus invalidating its previous
207/// value.
208#[derive(Debug)]
209pub struct TableIterator<'a>(*mut _CassIterator, PhantomData<&'a CassKeyspaceMeta>);
210
211// The underlying C type has no thread-local state, and forbids only concurrent
212// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
213unsafe impl Send for TableIterator<'_> {}
214unsafe impl Sync for TableIterator<'_> {}
215
216impl LendingIterator for TableIterator<'_> {
217    type Item<'a>
218        = TableMeta<'a>
219    where
220        Self: 'a;
221    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
222        unsafe {
223            match cass_iterator_next(self.0) {
224                cass_false => None,
225                cass_true => Some(TableMeta::build(cass_iterator_get_table_meta(self.0))),
226            }
227        }
228    }
229}
230
231/// Iterator over the keyspaces in the schema.
232///
233/// A Cassandra iterator is a `LendingIterator` because it borrows from some
234/// underlying value, but owns a single item. Each time `next()` is invoked it
235/// decodes the current item into that item, thus invalidating its previous
236/// value.
237#[derive(Debug)]
238pub struct KeyspaceIterator<'a>(*mut _CassIterator, PhantomData<&'a CassSchemaMeta>);
239
240// The underlying C type has no thread-local state, and forbids only concurrent
241// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
242unsafe impl Send for KeyspaceIterator<'_> {}
243unsafe impl Sync for KeyspaceIterator<'_> {}
244
245impl LendingIterator for KeyspaceIterator<'_> {
246    type Item<'a>
247        = KeyspaceMeta<'a>
248    where
249        Self: 'a;
250    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
251        unsafe {
252            match cass_iterator_next(self.0) {
253                cass_false => None,
254                cass_true => Some(KeyspaceMeta::build(cass_iterator_get_keyspace_meta(self.0))),
255            }
256        }
257    }
258}
259
260/// Iterator over the columns in a table.
261///
262/// A Cassandra iterator is a `LendingIterator` because it borrows from some
263/// underlying value, but owns a single item. Each time `next()` is invoked it
264/// decodes the current item into that item, thus invalidating its previous
265/// value.
266#[derive(Debug)]
267pub struct ColumnIterator<'a>(*mut _CassIterator, PhantomData<&'a CassTableMeta>);
268
269// The underlying C type has no thread-local state, and forbids only concurrent
270// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
271unsafe impl Send for ColumnIterator<'_> {}
272unsafe impl Sync for ColumnIterator<'_> {}
273
274impl LendingIterator for ColumnIterator<'_> {
275    type Item<'a>
276        = ColumnMeta<'a>
277    where
278        Self: 'a;
279    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
280        unsafe {
281            match cass_iterator_next(self.0) {
282                cass_false => None,
283                cass_true => Some(ColumnMeta::build(cass_iterator_get_column_meta(self.0))),
284            }
285        }
286    }
287}
288
289/// Iterator over the fields in a metadata object.
290///
291/// A Cassandra iterator is a `LendingIterator` because it borrows from some
292/// underlying value, but owns a single item. Each time `next()` is invoked it
293/// decodes the current item into that item, thus invalidating its previous
294/// value.
295//
296// Could be one of several underlying types; CassTableMeta is just a
297// representative. Since it's a phantom, it doesn't matter which type we name.
298#[derive(Debug)]
299pub struct FieldIterator<'a>(*mut _CassIterator, PhantomData<&'a CassTableMeta>);
300
301// The underlying C type has no thread-local state, and forbids only concurrent
302// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
303unsafe impl Send for FieldIterator<'_> {}
304unsafe impl Sync for FieldIterator<'_> {}
305
306impl LendingIterator for FieldIterator<'_> {
307    type Item<'a>
308        = Field<'a>
309    where
310        Self: 'a;
311
312    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
313        unsafe {
314            match cass_iterator_next(self.0) {
315                cass_false => None,
316                cass_true => {
317                    let mut name = std::ptr::null();
318                    let mut name_length = 0;
319                    cass_iterator_get_meta_field_name(self.0, &mut name, &mut name_length)
320                        .to_result(())
321                        .and_then(|_| {
322                            let slice = slice::from_raw_parts(name as *const u8, name_length);
323                            let name = str::from_utf8(slice)?.to_owned();
324                            let value = Value::build(cass_iterator_get_meta_field_value(self.0));
325                            Ok(Some(Field { name, value }))
326                        })
327                }
328                .expect("Cassandra error during iteration"),
329            }
330        }
331    }
332}
333
334impl ProtectedInner<*mut _CassIterator> for UserTypeIterator<'_> {
335    fn inner(&self) -> *mut _CassIterator {
336        self.0
337    }
338}
339
340impl Protected<*mut _CassIterator> for UserTypeIterator<'_> {
341    fn build(inner: *mut _CassIterator) -> Self {
342        if inner.is_null() {
343            panic!("Unexpected null pointer")
344        };
345        UserTypeIterator(inner, PhantomData)
346    }
347}
348
349impl ProtectedInner<*mut _CassIterator> for AggregateIterator<'_> {
350    fn inner(&self) -> *mut _CassIterator {
351        self.0
352    }
353}
354
355impl Protected<*mut _CassIterator> for AggregateIterator<'_> {
356    fn build(inner: *mut _CassIterator) -> Self {
357        if inner.is_null() {
358            panic!("Unexpected null pointer")
359        };
360        AggregateIterator(inner, PhantomData)
361    }
362}
363
364impl ProtectedInner<*mut _CassIterator> for FunctionIterator<'_> {
365    fn inner(&self) -> *mut _CassIterator {
366        self.0
367    }
368}
369
370impl Protected<*mut _CassIterator> for FunctionIterator<'_> {
371    fn build(inner: *mut _CassIterator) -> Self {
372        if inner.is_null() {
373            panic!("Unexpected null pointer")
374        };
375        FunctionIterator(inner, PhantomData)
376    }
377}
378
379impl ProtectedInner<*mut _CassIterator> for KeyspaceIterator<'_> {
380    fn inner(&self) -> *mut _CassIterator {
381        self.0
382    }
383}
384
385impl Protected<*mut _CassIterator> for KeyspaceIterator<'_> {
386    fn build(inner: *mut _CassIterator) -> Self {
387        if inner.is_null() {
388            panic!("Unexpected null pointer")
389        };
390        KeyspaceIterator(inner, PhantomData)
391    }
392}
393
394impl ProtectedInner<*mut _CassIterator> for FieldIterator<'_> {
395    fn inner(&self) -> *mut _CassIterator {
396        self.0
397    }
398}
399impl Protected<*mut _CassIterator> for FieldIterator<'_> {
400    fn build(inner: *mut _CassIterator) -> Self {
401        if inner.is_null() {
402            panic!("Unexpected null pointer")
403        };
404        FieldIterator(inner, PhantomData)
405    }
406}
407
408impl ProtectedInner<*mut _CassIterator> for ColumnIterator<'_> {
409    fn inner(&self) -> *mut _CassIterator {
410        self.0
411    }
412}
413
414impl Protected<*mut _CassIterator> for ColumnIterator<'_> {
415    fn build(inner: *mut _CassIterator) -> Self {
416        if inner.is_null() {
417            panic!("Unexpected null pointer")
418        };
419        ColumnIterator(inner, PhantomData)
420    }
421}
422
423impl ProtectedInner<*mut _CassIterator> for TableIterator<'_> {
424    fn inner(&self) -> *mut _CassIterator {
425        self.0
426    }
427}
428
429impl Protected<*mut _CassIterator> for TableIterator<'_> {
430    fn build(inner: *mut _CassIterator) -> Self {
431        if inner.is_null() {
432            panic!("Unexpected null pointer")
433        };
434        TableIterator(inner, PhantomData)
435    }
436}
437
438impl ProtectedInner<*mut _CassIterator> for MapIterator<'_> {
439    fn inner(&self) -> *mut _CassIterator {
440        self.0
441    }
442}
443
444impl Protected<*mut _CassIterator> for MapIterator<'_> {
445    fn build(inner: *mut _CassIterator) -> Self {
446        if inner.is_null() {
447            panic!("Unexpected null pointer")
448        };
449        MapIterator(inner, PhantomData)
450    }
451}
452
453impl ProtectedInner<*mut _CassIterator> for SetIterator<'_> {
454    fn inner(&self) -> *mut _CassIterator {
455        self.0
456    }
457}
458
459impl Protected<*mut _CassIterator> for SetIterator<'_> {
460    fn build(inner: *mut _CassIterator) -> Self {
461        if inner.is_null() {
462            panic!("Unexpected null pointer")
463        };
464        SetIterator(inner, PhantomData)
465    }
466}
467
468/// Iterator over the values in a set.
469///
470/// A Cassandra iterator is a `LendingIterator` because it borrows from some
471/// underlying value, but owns a single item. Each time `next()` is invoked it
472/// decodes the current item into that item, thus invalidating its previous
473/// value.
474#[derive(Debug)]
475pub struct SetIterator<'a>(*mut _CassIterator, PhantomData<&'a _CassValue>);
476
477// The underlying C type has no thread-local state, and forbids only concurrent
478// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
479unsafe impl Send for SetIterator<'_> {}
480unsafe impl Sync for SetIterator<'_> {}
481
482impl Drop for SetIterator<'_> {
483    fn drop(&mut self) {
484        unsafe { cass_iterator_free(self.0) }
485    }
486}
487
488impl LendingIterator for SetIterator<'_> {
489    type Item<'a>
490        = Value<'a>
491    where
492        Self: 'a;
493
494    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
495        unsafe {
496            match cass_iterator_next(self.0) {
497                cass_false => None,
498                cass_true => Some(self.get_value()),
499            }
500        }
501    }
502}
503
504impl SetIterator<'_> {
505    fn get_value(&self) -> Value<'_> {
506        unsafe { Value::build(cass_iterator_get_value(self.0)) }
507    }
508}
509
510/// An iterator over the k/v pairs in a map.
511#[derive(Debug)]
512///
513/// A Cassandra iterator is a `LendingIterator` because it borrows from some
514/// underlying value, but owns a single item. Each time `next()` is invoked it
515/// decodes the current item into that item, thus invalidating its previous
516/// value.
517pub struct MapIterator<'a>(*mut _CassIterator, PhantomData<&'a _CassValue>);
518
519// The underlying C type has no thread-local state, and forbids only concurrent
520// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
521unsafe impl Send for MapIterator<'_> {}
522unsafe impl Sync for MapIterator<'_> {}
523
524impl MapIterator<'_> {
525    fn get_key(&self) -> Value<'_> {
526        unsafe { Value::build(cass_iterator_get_map_key(self.0)) }
527    }
528    fn get_value(&self) -> Value<'_> {
529        unsafe { Value::build(cass_iterator_get_map_value(self.0)) }
530    }
531
532    /// Gets the next k/v pair in the map
533    pub fn get_pair(&self) -> (Value<'_>, Value<'_>) {
534        (self.get_key(), self.get_value())
535    }
536}
537
538impl Drop for MapIterator<'_> {
539    fn drop(&mut self) {
540        unsafe { cass_iterator_free(self.0) }
541    }
542}
543
544impl LendingIterator for MapIterator<'_> {
545    type Item<'a>
546        = (Value<'a>, Value<'a>)
547    where
548        Self: 'a;
549    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
550        unsafe {
551            match cass_iterator_next(self.0) {
552                cass_false => None,
553                cass_true => Some(self.get_pair()),
554            }
555        }
556    }
557}