cassandra_cpp/cassandra/
result.rs

1#![allow(non_camel_case_types)]
2#![allow(dead_code)]
3#![allow(missing_copy_implementations)]
4
5use crate::cassandra::data_type::ConstDataType;
6use crate::cassandra::error::*;
7use crate::cassandra::iterator::LendingIterator;
8use crate::cassandra::row::Row;
9use crate::cassandra::util::{Protected, ProtectedInner};
10use crate::cassandra::value::ValueType;
11
12use crate::cassandra_sys::cass_false;
13use crate::cassandra_sys::cass_iterator_free;
14use crate::cassandra_sys::cass_iterator_from_result;
15use crate::cassandra_sys::cass_iterator_get_row;
16use crate::cassandra_sys::cass_iterator_next;
17use crate::cassandra_sys::cass_result_column_count;
18use crate::cassandra_sys::cass_result_column_data_type;
19use crate::cassandra_sys::cass_result_column_name;
20use crate::cassandra_sys::cass_result_column_type;
21use crate::cassandra_sys::cass_result_first_row;
22use crate::cassandra_sys::cass_result_free;
23use crate::cassandra_sys::cass_result_has_more_pages;
24use crate::cassandra_sys::cass_result_paging_state_token;
25use crate::cassandra_sys::cass_result_row_count;
26use crate::cassandra_sys::cass_true;
27use crate::cassandra_sys::CassIterator as _CassIterator;
28use crate::cassandra_sys::CassResult as _CassResult;
29
30use std::fmt;
31use std::fmt::Debug;
32use std::fmt::Display;
33use std::fmt::Formatter;
34use std::marker::PhantomData;
35
36use std::slice;
37use std::str;
38
39/// The result of a query.
40/// A result object is read-only and is thread-safe to read or iterate over
41/// concurrently, since we do not bind any setters (e.g., `set_metadata`).
42pub struct CassResult(*const _CassResult);
43unsafe impl Sync for CassResult {}
44unsafe impl Send for CassResult {}
45
46impl ProtectedInner<*const _CassResult> for CassResult {
47    fn inner(&self) -> *const _CassResult {
48        self.0
49    }
50}
51
52impl Protected<*const _CassResult> for CassResult {
53    fn build(inner: *const _CassResult) -> Self {
54        if inner.is_null() {
55            panic!("Unexpected null pointer")
56        };
57        CassResult(inner)
58    }
59}
60
61impl Debug for CassResult {
62    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
63        writeln!(f, "Result row count: {:?}", self.row_count())?;
64        let mut iter = self.iter();
65        while let Some(row) = iter.next() {
66            writeln!(f, "{:?}", row)?;
67        }
68        Ok(())
69    }
70}
71
72impl Display for CassResult {
73    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
74        writeln!(f, "Result row count: {}", self.row_count())?;
75        let mut iter = self.iter();
76        while let Some(row) = iter.next() {
77            writeln!(f, "{}", row)?;
78        }
79        Ok(())
80    }
81}
82
83impl Drop for CassResult {
84    fn drop(&mut self) {
85        unsafe { cass_result_free(self.0) }
86    }
87}
88
89impl CassResult {
90    /// Gets the number of rows for the specified result.
91    pub fn row_count(&self) -> u64 {
92        unsafe { cass_result_row_count(self.0) as u64 }
93    }
94
95    /// Gets the number of columns per row for the specified result.
96    pub fn column_count(&self) -> u64 {
97        unsafe { cass_result_column_count(self.0) as u64 }
98    }
99
100    /// Gets the column name at index for the specified result.
101    pub fn column_name(&self, index: usize) -> Result<&str> {
102        let mut name = std::ptr::null();
103        let mut name_length = 0;
104        unsafe {
105            cass_result_column_name(self.0, index, &mut name, &mut name_length)
106                .to_result(())
107                .and_then(|_| {
108                    let slice = slice::from_raw_parts(name as *const u8, name_length);
109                    Ok(str::from_utf8(slice)?)
110                })
111        }
112    }
113
114    /// Gets the column type at index for the specified result.
115    pub fn column_type(&self, index: usize) -> ValueType {
116        unsafe { ValueType::build(cass_result_column_type(self.0, index)) }
117    }
118
119    /// Gets the column datatype at index for the specified result.
120    pub fn column_data_type(&self, index: usize) -> ConstDataType {
121        // TODO: can return NULL
122        unsafe { ConstDataType::build(cass_result_column_data_type(self.0, index)) }
123    }
124
125    /// Gets the first row of the result.
126    pub fn first_row(&self) -> Option<Row> {
127        unsafe {
128            match self.row_count() {
129                0 => None,
130                _ => Some(Row::build(cass_result_first_row(self.0))),
131            }
132        }
133    }
134
135    /// Returns true if there are more pages.
136    pub fn has_more_pages(&self) -> bool {
137        unsafe { cass_result_has_more_pages(self.0) == cass_true }
138    }
139
140    /// Gets the statement's paging state. This can be used to get the next page of
141    /// data in a multi-page query, by using `set_paging_state_token`.
142    ///
143    /// Returns:
144    ///   - `Ok(None)` if there are no more pages, and thus no paging state token.
145    ///   - `Ok(Some(Vec<u8>))` if there are more pages, and a paging state token.
146    ///   - `Err(_)` if there was an error getting the paging state token.
147    ///
148    /// [`set_paging_state_token`]: Statement::set_paging_state_token
149    pub fn paging_state_token(&self) -> Result<Option<Vec<u8>>> {
150        if !self.has_more_pages() {
151            return Ok(None);
152        }
153
154        let mut token_ptr = std::ptr::null();
155        let mut token_length = 0;
156        unsafe {
157            cass_result_paging_state_token(self.0, &mut token_ptr, &mut token_length)
158                .to_result(())
159                .map(|_| Some(slice::from_raw_parts(token_ptr as *const u8, token_length).to_vec()))
160        }
161    }
162
163    /// Creates a new iterator for the specified result. This can be
164    /// used to iterate over rows in the result.
165    pub fn iter(&self) -> ResultIterator {
166        unsafe {
167            ResultIterator(
168                cass_iterator_from_result(self.0),
169                cass_result_row_count(self.0),
170                PhantomData,
171            )
172        }
173    }
174}
175
176/// An iterator over the results of a query. The result holds the data, so
177/// the result must last for at least the lifetime of the iterator.
178///
179/// This is a lending iterator (you must stop using each item before you move to
180/// the next), and so it does not implement `std::iter::Iterator`. The best way
181/// to use it is as follows:
182///
183/// ```no_run
184/// # use cassandra_cpp::*;
185/// # let result: CassResult = unimplemented!();
186/// let mut iter = result.iter();
187/// while let Some(row) = iter.next() {
188///   // ... do something with `row` ...
189/// }
190/// ```
191///
192/// A Cassandra iterator is a `LendingIterator` because it borrows from some
193/// underlying value, but owns a single item. Each time `next()` is invoked it
194/// decodes the current item into that item, thus invalidating its previous
195/// value.
196#[derive(Debug)]
197pub struct ResultIterator<'a>(*mut _CassIterator, usize, PhantomData<&'a _CassResult>);
198
199// The underlying C type has no thread-local state, and forbids only concurrent
200// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety
201unsafe impl Send for ResultIterator<'_> {}
202unsafe impl Sync for ResultIterator<'_> {}
203
204impl<'a> Drop for ResultIterator<'a> {
205    fn drop(&mut self) {
206        unsafe { cass_iterator_free(self.0) }
207    }
208}
209
210impl LendingIterator for ResultIterator<'_> {
211    type Item<'a> = Row<'a> where Self: 'a;
212
213    fn next(&mut self) -> Option<<Self as LendingIterator>::Item<'_>> {
214        unsafe {
215            match cass_iterator_next(self.0) {
216                cass_false => None,
217                cass_true => Some(self.get_row()),
218            }
219        }
220    }
221
222    fn size_hint(&self) -> (usize, Option<usize>) {
223        (0, Some(self.1))
224    }
225}
226
227impl ResultIterator<'_> {
228    /// Gets the current row in the result set
229    pub fn get_row(&self) -> Row {
230        unsafe { Row::build(cass_iterator_get_row(self.0)) }
231    }
232}