1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use crate::DocumentCollection;
use crate::{QldbResult, QueryBuilder};
use std::convert::TryInto;

/// Cursor allows to get all values from a statement page by page.
///
/// QLDB returns 200 documents for each page.
///
/// You don't need to directly use Cursor in your code. When the
/// method [](crate::QueryBuilder::execute) uses Cursor internally
/// in order to load all values.
///
/// ```rust,no_run
/// use qldb::{QldbClient, Cursor};
/// # use std::collections::HashMap;
/// # use eyre::Result;
///
/// # async fn test() -> Result<()> {
/// let client = QldbClient::default("rust-crate-test", 200).await?;
///
/// let mut value_to_insert = HashMap::new();
/// // This will insert a documents with a key "test_column"
/// // with the value "IonValue::String(test_value)"
/// value_to_insert.insert("test_column", "test_value");
///
/// client
///     .transaction_within(|client| async move {   
///         let mut cursor = client
///             .query("SEL/CT * FROM TestTable")
///             .get_cursor()?;
///             
///             while let Some(mut values) = cursor.load_more().await? {
///                 println!("{:?}", values);
///             }
///
///         Ok(())
///     })
///     .await?;
/// # Ok(())
/// # }
/// ```
///
#[derive(Debug)]
pub struct Cursor {
    query_builder: QueryBuilder,
    next_page: Option<String>,
    is_first_page: bool,
}

impl Cursor {
    pub(crate) fn new(query_builder: QueryBuilder) -> Cursor {
        Cursor {
            query_builder,
            next_page: None,
            is_first_page: true,
        }
    }

    /// It loads the next page from a query. It automatically tracks
    /// the next_page_token, so you can call this method again and
    /// again in order to load all pages.
    ///
    /// It returns Ok(Some(_)) when QLDB returns documents.
    ///
    /// It returns Ok(None) when QLDB doesn't return documents,
    /// which means that there isn't more pages to query
    ///
    /// ```rust,no_run
    /// # use qldb::{Cursor, QldbResult};
    ///
    /// # async fn test(mut cursor: Cursor) ->  QldbResult<()> {
    ///     while let Some(mut values) = cursor.load_more().await? {
    ///         println!("{:?}", values);
    ///     }
    ///     
    /// #   Ok(())
    /// # }
    ///
    /// ```
    pub async fn load_more(&mut self) -> QldbResult<Option<DocumentCollection>> {
        let (values, next_page_token) = if self.is_first_page {
            self.query_builder.execute_statement().await?
        } else if let Some(page) = &self.next_page {
            self.query_builder.execute_get_page(&page).await?
        } else {
            self.is_first_page = false;
            return Ok(None);
        };

        self.is_first_page = false;

        self.next_page = next_page_token;

        Ok(Some(values.try_into()?))
    }

    /// Loads all pages from the cursor and consumes it in the process.
    pub async fn load_all(mut self) -> QldbResult<DocumentCollection> {
        let mut result = DocumentCollection::new(vec![]);

        while let Some(values) = self.load_more().await? {
            result.extend(values.into_iter());

            if self.next_page.is_none() {
                break;
            }
        }

        Ok(result)
    }
}