discord_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::row::Row;
8use crate::cassandra::util::{Protected, ProtectedInner};
9use crate::cassandra::value::ValueType;
10
11use crate::cassandra_sys::cass_false;
12use crate::cassandra_sys::cass_iterator_free;
13use crate::cassandra_sys::cass_iterator_from_result;
14use crate::cassandra_sys::cass_iterator_get_row;
15use crate::cassandra_sys::cass_iterator_next;
16use crate::cassandra_sys::cass_result_column_count;
17use crate::cassandra_sys::cass_result_column_data_type;
18use crate::cassandra_sys::cass_result_column_name;
19use crate::cassandra_sys::cass_result_column_type;
20use crate::cassandra_sys::cass_result_first_row;
21use crate::cassandra_sys::cass_result_free;
22use crate::cassandra_sys::cass_result_has_more_pages;
23use crate::cassandra_sys::cass_result_paging_state_token;
24use crate::cassandra_sys::cass_result_row_count;
25use crate::cassandra_sys::cass_true;
26use crate::cassandra_sys::CassIterator as _CassIterator;
27use crate::cassandra_sys::CassResult as _CassResult;
28
29use std::ffi::CString;
30use std::fmt;
31use std::fmt::Debug;
32use std::fmt::Display;
33use std::fmt::Formatter;
34use std::marker::PhantomData;
35use std::mem;
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        write!(f, "Result row count: {:?}\n", self.row_count())?;
64        for row in self.iter() {
65            write!(f, "{:?}\n", row)?;
66        }
67        Ok(())
68    }
69}
70
71impl Display for CassResult {
72    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
73        write!(f, "Result row count: {}\n", self.row_count())?;
74        for row in self.iter() {
75            write!(f, "{}\n", row)?;
76        }
77        Ok(())
78    }
79}
80
81impl Drop for CassResult {
82    fn drop(&mut self) {
83        unsafe { cass_result_free(self.0) }
84    }
85}
86
87impl CassResult {
88    /// Gets the number of rows for the specified result.
89    pub fn row_count(&self) -> u64 {
90        unsafe { cass_result_row_count(self.0) as u64 }
91    }
92
93    /// Gets the number of columns per row for the specified result.
94    pub fn column_count(&self) -> u64 {
95        unsafe { cass_result_column_count(self.0) as u64 }
96    }
97
98    /// Gets the column name at index for the specified result.
99    pub fn column_name(&self, index: usize) -> Result<&str> {
100        unsafe {
101            let mut name = mem::zeroed();
102            let mut name_length = mem::zeroed();
103            cass_result_column_name(self.0, index, &mut name, &mut name_length)
104                .to_result(())
105                .and_then(|_| {
106                    let slice = slice::from_raw_parts(name as *const u8, name_length as usize);
107                    Ok(str::from_utf8(slice)?)
108                })
109        }
110    }
111
112    /// Gets the column type at index for the specified result.
113    pub fn column_type(&self, index: usize) -> ValueType {
114        unsafe { ValueType::build(cass_result_column_type(self.0, index)) }
115    }
116
117    /// Gets the column datatype at index for the specified result.
118    pub fn column_data_type(&self, index: usize) -> ConstDataType {
119        // TODO: can return NULL
120        unsafe { ConstDataType::build(cass_result_column_data_type(self.0, index)) }
121    }
122
123    /// Gets the first row of the result.
124    pub fn first_row(&self) -> Option<Row> {
125        unsafe {
126            match self.row_count() {
127                0 => None,
128                _ => Some(Row::build(cass_result_first_row(self.0))),
129            }
130        }
131    }
132
133    /// Returns true if there are more pages.
134    pub fn has_more_pages(&self) -> bool {
135        unsafe { cass_result_has_more_pages(self.0) == cass_true }
136    }
137
138    /// Gets the statement's paging state. This can be used to get the next page of
139    /// data in a multi-page query, by using `set_paging_state_token`.
140    ///
141    /// Returns:
142    ///   - `Ok(None)` if there are no more pages, and thus no paging state token.
143    ///   - `Ok(Some(Vec<u8>)) if there are more pages, and a paging state token.
144    ///   - `Err(_)` if there was an error getting the paging state token.
145    ///
146    /// [`set_paging_state_token`]: Statement::set_paging_state_token
147    pub fn paging_state_token(&self) -> Result<Option<Vec<u8>>> {
148        if !self.has_more_pages() {
149            return Ok(None);
150        }
151
152        unsafe {
153            let mut token_ptr = mem::zeroed();
154            let mut token_length = mem::zeroed();
155            cass_result_paging_state_token(
156                self.0,
157                &mut token_ptr,
158                &mut token_length,
159            )
160            .to_result(())
161            .map(|_| {
162                    Some(
163                        slice::from_raw_parts(token_ptr as *const u8, token_length as usize)
164                            .to_vec(),
165                    )
166                })
167        }
168    }
169
170    /// Creates a new iterator for the specified result. This can be
171    /// used to iterate over rows in the result.
172    pub fn iter(&self) -> ResultIterator {
173        unsafe {
174            ResultIterator(
175                cass_iterator_from_result(self.0),
176                cass_result_row_count(self.0),
177                PhantomData,
178            )
179        }
180    }
181}
182
183/// An iterator over the results of a query.
184/// The result holds the data, so it must last for at least the lifetime of the iterator.
185#[derive(Debug)]
186pub struct ResultIterator<'a>(pub *mut _CassIterator, usize, PhantomData<&'a CassResult>);
187
188// The underlying C type has no thread-local state, but does not support access
189// from multiple threads: https://datastax.github.io/cpp-driver/topics/#thread-safety
190unsafe impl<'a> Send for ResultIterator<'a> {}
191
192impl<'a> Drop for ResultIterator<'a> {
193    fn drop(&mut self) {
194        unsafe { cass_iterator_free(self.0) }
195    }
196}
197
198impl<'a> Iterator for ResultIterator<'a> {
199    type Item = Row<'a>;
200    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
201        unsafe {
202            match cass_iterator_next(self.0) {
203                cass_false => None,
204                cass_true => Some(self.get_row()),
205            }
206        }
207    }
208
209    fn size_hint(&self) -> (usize, Option<usize>) {
210        (0, Some(self.1))
211    }
212}
213
214impl<'a> ResultIterator<'a> {
215    /// Gets the next row in the result set
216    pub fn get_row(&mut self) -> Row<'a> {
217        unsafe { Row::build(cass_iterator_get_row(self.0)) }
218    }
219}
220
221impl<'a> IntoIterator for &'a CassResult {
222    type Item = Row<'a>;
223    type IntoIter = ResultIterator<'a>;
224
225    fn into_iter(self) -> Self::IntoIter {
226        self.iter()
227    }
228}