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> = AggregateMeta<'a> where Self: 'a;
101    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
102        unsafe {
103            match cass_iterator_next(self.0) {
104                cass_false => None,
105                cass_true => {
106                    let field_value = cass_iterator_get_aggregate_meta(self.0);
107                    Some(AggregateMeta::build(field_value))
108                }
109            }
110        }
111    }
112}
113
114/// Iterator over the fields of a UDT
115///
116/// A Cassandra iterator is a `LendingIterator` because it borrows from some
117/// underlying value, but owns a single item. Each time `next()` is invoked it
118/// decodes the current item into that item, thus invalidating its previous
119/// value.
120#[derive(Debug)]
121pub struct UserTypeIterator<'a>(*mut _CassIterator, PhantomData<&'a _CassValue>);
122
123// The underlying C type has no thread-local state, and forbids only concurrent
124// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
125unsafe impl Send for UserTypeIterator<'_> {}
126unsafe impl Sync for UserTypeIterator<'_> {}
127
128impl Drop for UserTypeIterator<'_> {
129    fn drop(&mut self) {
130        unsafe { cass_iterator_free(self.0) }
131    }
132}
133
134impl LendingIterator for UserTypeIterator<'_> {
135    type Item<'a> = (String, Value<'a>) where Self: 'a;
136    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
137        unsafe {
138            match cass_iterator_next(self.0) {
139                cass_false => None,
140                cass_true => Some((self.get_field_name(), self.get_field_value())),
141            }
142        }
143    }
144}
145
146impl UserTypeIterator<'_> {
147    fn get_field_name(&self) -> String {
148        unsafe {
149            let mut name = std::ptr::null();
150            let mut name_length = 0;
151            cass_iterator_get_user_type_field_name(self.0, &mut name, &mut name_length)
152                .to_result(())
153                .and_then(|_| {
154                    let slice = slice::from_raw_parts(name as *const u8, name_length);
155                    let name = str::from_utf8(slice)?.to_owned();
156                    Ok(name)
157                })
158                .expect("Cassandra error during iteration")
159        }
160    }
161
162    fn get_field_value(&self) -> Value {
163        unsafe { Value::build(cass_iterator_get_user_type_field_value(self.0)) }
164    }
165}
166
167/// Iterator over the functions in a keyspace.
168///
169/// A Cassandra iterator is a `LendingIterator` because it borrows from some
170/// underlying value, but owns a single item. Each time `next()` is invoked it
171/// decodes the current item into that item, thus invalidating its previous
172/// value.
173#[derive(Debug)]
174pub struct FunctionIterator<'a>(*mut _CassIterator, PhantomData<&'a CassKeyspaceMeta>);
175
176// The underlying C type has no thread-local state, and forbids only concurrent
177// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
178unsafe impl Send for FunctionIterator<'_> {}
179unsafe impl Sync for FunctionIterator<'_> {}
180
181impl LendingIterator for FunctionIterator<'_> {
182    type Item<'a> = FunctionMeta<'a> where Self: 'a;
183    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
184        unsafe {
185            match cass_iterator_next(self.0) {
186                cass_false => None,
187                cass_true => Some(FunctionMeta::build(cass_iterator_get_function_meta(self.0))),
188            }
189        }
190    }
191}
192
193/// Iterator over the tables in a keyspace.
194///
195/// A Cassandra iterator is a `LendingIterator` because it borrows from some
196/// underlying value, but owns a single item. Each time `next()` is invoked it
197/// decodes the current item into that item, thus invalidating its previous
198/// value.
199#[derive(Debug)]
200pub struct TableIterator<'a>(*mut _CassIterator, PhantomData<&'a CassKeyspaceMeta>);
201
202// The underlying C type has no thread-local state, and forbids only concurrent
203// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
204unsafe impl Send for TableIterator<'_> {}
205unsafe impl Sync for TableIterator<'_> {}
206
207impl LendingIterator for TableIterator<'_> {
208    type Item<'a> = TableMeta<'a> where Self: 'a;
209    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
210        unsafe {
211            match cass_iterator_next(self.0) {
212                cass_false => None,
213                cass_true => Some(TableMeta::build(cass_iterator_get_table_meta(self.0))),
214            }
215        }
216    }
217}
218
219/// Iterator over the keyspaces in the schema.
220///
221/// A Cassandra iterator is a `LendingIterator` because it borrows from some
222/// underlying value, but owns a single item. Each time `next()` is invoked it
223/// decodes the current item into that item, thus invalidating its previous
224/// value.
225#[derive(Debug)]
226pub struct KeyspaceIterator<'a>(*mut _CassIterator, PhantomData<&'a CassSchemaMeta>);
227
228// The underlying C type has no thread-local state, and forbids only concurrent
229// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
230unsafe impl Send for KeyspaceIterator<'_> {}
231unsafe impl Sync for KeyspaceIterator<'_> {}
232
233impl LendingIterator for KeyspaceIterator<'_> {
234    type Item<'a> = KeyspaceMeta<'a> where Self: 'a;
235    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
236        unsafe {
237            match cass_iterator_next(self.0) {
238                cass_false => None,
239                cass_true => Some(KeyspaceMeta::build(cass_iterator_get_keyspace_meta(self.0))),
240            }
241        }
242    }
243}
244
245/// Iterator over the columns in a table.
246///
247/// A Cassandra iterator is a `LendingIterator` because it borrows from some
248/// underlying value, but owns a single item. Each time `next()` is invoked it
249/// decodes the current item into that item, thus invalidating its previous
250/// value.
251#[derive(Debug)]
252pub struct ColumnIterator<'a>(*mut _CassIterator, PhantomData<&'a CassTableMeta>);
253
254// The underlying C type has no thread-local state, and forbids only concurrent
255// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
256unsafe impl Send for ColumnIterator<'_> {}
257unsafe impl Sync for ColumnIterator<'_> {}
258
259impl LendingIterator for ColumnIterator<'_> {
260    type Item<'a> = ColumnMeta<'a> where Self: 'a;
261    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
262        unsafe {
263            match cass_iterator_next(self.0) {
264                cass_false => None,
265                cass_true => Some(ColumnMeta::build(cass_iterator_get_column_meta(self.0))),
266            }
267        }
268    }
269}
270
271/// Iterator over the fields in a metadata object.
272///
273/// A Cassandra iterator is a `LendingIterator` because it borrows from some
274/// underlying value, but owns a single item. Each time `next()` is invoked it
275/// decodes the current item into that item, thus invalidating its previous
276/// value.
277//
278// Could be one of several underlying types; CassTableMeta is just a
279// representative. Since it's a phantom, it doesn't matter which type we name.
280#[derive(Debug)]
281pub struct FieldIterator<'a>(*mut _CassIterator, PhantomData<&'a CassTableMeta>);
282
283// The underlying C type has no thread-local state, and forbids only concurrent
284// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
285unsafe impl Send for FieldIterator<'_> {}
286unsafe impl Sync for FieldIterator<'_> {}
287
288impl LendingIterator for FieldIterator<'_> {
289    type Item<'a> = Field<'a> where Self: 'a;
290
291    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
292        unsafe {
293            match cass_iterator_next(self.0) {
294                cass_false => None,
295                cass_true => {
296                    let mut name = std::ptr::null();
297                    let mut name_length = 0;
298                    cass_iterator_get_meta_field_name(self.0, &mut name, &mut name_length)
299                        .to_result(())
300                        .and_then(|_| {
301                            let slice = slice::from_raw_parts(name as *const u8, name_length);
302                            let name = str::from_utf8(slice)?.to_owned();
303                            let value = Value::build(cass_iterator_get_meta_field_value(self.0));
304                            Ok(Some(Field { name, value }))
305                        })
306                }
307                .expect("Cassandra error during iteration"),
308            }
309        }
310    }
311}
312
313impl ProtectedInner<*mut _CassIterator> for UserTypeIterator<'_> {
314    fn inner(&self) -> *mut _CassIterator {
315        self.0
316    }
317}
318
319impl Protected<*mut _CassIterator> for UserTypeIterator<'_> {
320    fn build(inner: *mut _CassIterator) -> Self {
321        if inner.is_null() {
322            panic!("Unexpected null pointer")
323        };
324        UserTypeIterator(inner, PhantomData)
325    }
326}
327
328impl ProtectedInner<*mut _CassIterator> for AggregateIterator<'_> {
329    fn inner(&self) -> *mut _CassIterator {
330        self.0
331    }
332}
333
334impl Protected<*mut _CassIterator> for AggregateIterator<'_> {
335    fn build(inner: *mut _CassIterator) -> Self {
336        if inner.is_null() {
337            panic!("Unexpected null pointer")
338        };
339        AggregateIterator(inner, PhantomData)
340    }
341}
342
343impl ProtectedInner<*mut _CassIterator> for FunctionIterator<'_> {
344    fn inner(&self) -> *mut _CassIterator {
345        self.0
346    }
347}
348
349impl Protected<*mut _CassIterator> for FunctionIterator<'_> {
350    fn build(inner: *mut _CassIterator) -> Self {
351        if inner.is_null() {
352            panic!("Unexpected null pointer")
353        };
354        FunctionIterator(inner, PhantomData)
355    }
356}
357
358impl ProtectedInner<*mut _CassIterator> for KeyspaceIterator<'_> {
359    fn inner(&self) -> *mut _CassIterator {
360        self.0
361    }
362}
363
364impl Protected<*mut _CassIterator> for KeyspaceIterator<'_> {
365    fn build(inner: *mut _CassIterator) -> Self {
366        if inner.is_null() {
367            panic!("Unexpected null pointer")
368        };
369        KeyspaceIterator(inner, PhantomData)
370    }
371}
372
373impl ProtectedInner<*mut _CassIterator> for FieldIterator<'_> {
374    fn inner(&self) -> *mut _CassIterator {
375        self.0
376    }
377}
378impl Protected<*mut _CassIterator> for FieldIterator<'_> {
379    fn build(inner: *mut _CassIterator) -> Self {
380        if inner.is_null() {
381            panic!("Unexpected null pointer")
382        };
383        FieldIterator(inner, PhantomData)
384    }
385}
386
387impl ProtectedInner<*mut _CassIterator> for ColumnIterator<'_> {
388    fn inner(&self) -> *mut _CassIterator {
389        self.0
390    }
391}
392
393impl Protected<*mut _CassIterator> for ColumnIterator<'_> {
394    fn build(inner: *mut _CassIterator) -> Self {
395        if inner.is_null() {
396            panic!("Unexpected null pointer")
397        };
398        ColumnIterator(inner, PhantomData)
399    }
400}
401
402impl ProtectedInner<*mut _CassIterator> for TableIterator<'_> {
403    fn inner(&self) -> *mut _CassIterator {
404        self.0
405    }
406}
407
408impl Protected<*mut _CassIterator> for TableIterator<'_> {
409    fn build(inner: *mut _CassIterator) -> Self {
410        if inner.is_null() {
411            panic!("Unexpected null pointer")
412        };
413        TableIterator(inner, PhantomData)
414    }
415}
416
417impl ProtectedInner<*mut _CassIterator> for MapIterator<'_> {
418    fn inner(&self) -> *mut _CassIterator {
419        self.0
420    }
421}
422
423impl Protected<*mut _CassIterator> for MapIterator<'_> {
424    fn build(inner: *mut _CassIterator) -> Self {
425        if inner.is_null() {
426            panic!("Unexpected null pointer")
427        };
428        MapIterator(inner, PhantomData)
429    }
430}
431
432impl ProtectedInner<*mut _CassIterator> for SetIterator<'_> {
433    fn inner(&self) -> *mut _CassIterator {
434        self.0
435    }
436}
437
438impl Protected<*mut _CassIterator> for SetIterator<'_> {
439    fn build(inner: *mut _CassIterator) -> Self {
440        if inner.is_null() {
441            panic!("Unexpected null pointer")
442        };
443        SetIterator(inner, PhantomData)
444    }
445}
446
447/// Iterator over the values in a set.
448///
449/// A Cassandra iterator is a `LendingIterator` because it borrows from some
450/// underlying value, but owns a single item. Each time `next()` is invoked it
451/// decodes the current item into that item, thus invalidating its previous
452/// value.
453#[derive(Debug)]
454pub struct SetIterator<'a>(*mut _CassIterator, PhantomData<&'a _CassValue>);
455
456// The underlying C type has no thread-local state, and forbids only concurrent
457// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
458unsafe impl Send for SetIterator<'_> {}
459unsafe impl Sync for SetIterator<'_> {}
460
461impl Drop for SetIterator<'_> {
462    fn drop(&mut self) {
463        unsafe { cass_iterator_free(self.0) }
464    }
465}
466
467impl LendingIterator for SetIterator<'_> {
468    type Item<'a> = Value<'a> where Self: 'a;
469
470    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
471        unsafe {
472            match cass_iterator_next(self.0) {
473                cass_false => None,
474                cass_true => Some(self.get_value()),
475            }
476        }
477    }
478}
479
480impl SetIterator<'_> {
481    fn get_value(&self) -> Value {
482        unsafe { Value::build(cass_iterator_get_value(self.0)) }
483    }
484}
485
486/// An iterator over the k/v pairs in a map.
487#[derive(Debug)]
488///
489/// A Cassandra iterator is a `LendingIterator` because it borrows from some
490/// underlying value, but owns a single item. Each time `next()` is invoked it
491/// decodes the current item into that item, thus invalidating its previous
492/// value.
493pub struct MapIterator<'a>(*mut _CassIterator, PhantomData<&'a _CassValue>);
494
495// The underlying C type has no thread-local state, and forbids only concurrent
496// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
497unsafe impl Send for MapIterator<'_> {}
498unsafe impl Sync for MapIterator<'_> {}
499
500impl MapIterator<'_> {
501    fn get_key(&self) -> Value {
502        unsafe { Value::build(cass_iterator_get_map_key(self.0)) }
503    }
504    fn get_value(&self) -> Value {
505        unsafe { Value::build(cass_iterator_get_map_value(self.0)) }
506    }
507
508    /// Gets the next k/v pair in the map
509    pub fn get_pair(&self) -> (Value, Value) {
510        (self.get_key(), self.get_value())
511    }
512}
513
514impl Drop for MapIterator<'_> {
515    fn drop(&mut self) {
516        unsafe { cass_iterator_free(self.0) }
517    }
518}
519
520impl LendingIterator for MapIterator<'_> {
521    type Item<'a> = (Value<'a>, Value<'a>) where Self: 'a;
522    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
523        unsafe {
524            match cass_iterator_next(self.0) {
525                cass_false => None,
526                cass_true => Some(self.get_pair()),
527            }
528        }
529    }
530}