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)
    }
}