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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
use std::{
    fmt::Display,
    ops::{Deref, DerefMut},
};

use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
use bson::Document;
use serde::{Deserialize, Serialize};

use crate::option::CursorOptions;

/// Represents a Cursor to an Item with no special direction.
/// To Debug the contents, use `Debug`
/// When serializing or converting to String, the [`Edge`] gets encoded as url-safe Base64 String.
#[derive(Clone, Debug, PartialEq)]
pub struct Edge(Document);

impl Edge {
    /// Creates a new [`Edge`] using a value Document and the sorting keys.
    /// Only retains the values of the keys specified in the sort options to optimize storage.
    ///
    /// # Arguments
    /// * `document`: The Item to which the Edge will point to
    /// * `options`: Used to extract the sorting keys
    pub fn new(document: Document, options: &CursorOptions) -> Self {
        let mut cursor = Document::new();
        options
            .sort
            .clone()
            .unwrap_or_default()
            .keys()
            .filter_map(|key| document.get(key).map(|value| (key, value)))
            .for_each(|(key, value)| {
                cursor.insert(key, value);
            });
        Self(cursor)
    }
}

impl Display for Edge {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            &URL_SAFE_NO_PAD.encode(bson::to_vec(&self.0).map_err(serde::ser::Error::custom)?)
        )
    }
}

impl Serialize for Edge {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(
            &URL_SAFE_NO_PAD.encode(bson::to_vec(&self.0).map_err(serde::ser::Error::custom)?),
        )
    }
}

impl<'de> Deserialize<'de> for Edge {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Vis;
        impl serde::de::Visitor<'_> for Vis {
            type Value = Edge;

            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("a base64 string")
            }

            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
                let doc = URL_SAFE_NO_PAD
                    .decode(v)
                    .map_err(serde::de::Error::custom)?;
                Ok(Edge(
                    bson::from_slice(doc.as_slice()).map_err(serde::de::Error::custom)?,
                ))
            }
        }
        deserializer.deserialize_str(Vis)
    }
}

#[cfg(feature = "graphql")]
#[juniper::object]
impl Edge {
    fn cursor(&self) -> String {
        self.cursor.to_owned()
    }
}

impl Deref for Edge {
    type Target = Document;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Edge {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

/// Contains information about the current Page
/// The cursor have the respecting direction to be used as is in an subsequent find:
/// start_cursor: `Backwards`
/// end_cursor: `Forward`
///
/// Note: has_xxx means if the next page has items, not if there is a next cursor
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
pub struct PageInfo {
    /// True if there is a previous page which contains items
    pub has_previous_page: bool,
    /// True if there is a next page which contains items
    pub has_next_page: bool,
    /// Cursor to the first item of the page. Is set even when there is no previous page.
    pub start_cursor: Option<DirectedCursor>,
    /// Cursor to the last item of the page. Is set even when there is no next page.
    pub end_cursor: Option<DirectedCursor>,
}

#[cfg(feature = "graphql")]
#[juniper::object]
impl PageInfo {
    fn has_next_page(&self) -> bool {
        self.has_next_page
    }

    fn has_previous_page(&self) -> bool {
        self.has_previous_page
    }

    fn start_cursor(&self) -> Option<String> {
        self.start_cursor.to_owned()
    }

    fn end_cursor(&self) -> Option<String> {
        self.next_cursor.to_owned()
    }
}

/// The result of a find method with the items, edges, pagination info, and total count of objects
#[derive(Debug, Default)]
pub struct FindResult<T> {
    /// Current Page
    pub page_info: PageInfo,
    /// Edges to all items in the current Page, including start & end-cursor
    pub edges: Vec<Edge>,
    /// Total count of items in the whole collection
    pub total_count: u64,
    /// All items in the current Page
    pub items: Vec<T>,
}

/// Cursor to an item with direction information.
/// Serializing pretains the direction Information.
/// To send only the Cursor use `to_string` which drops the direction information
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum DirectedCursor {
    /// Use to invert the search e.g. go back a page
    Backwards(Edge),
    /// Normal direction to search
    Forward(Edge),
}

impl DirectedCursor {
    /// Reverses the direction of Cursor.
    pub fn reverse(self) -> Self {
        match self {
            Self::Backwards(edge) => Self::Forward(edge),
            Self::Forward(edge) => Self::Backwards(edge),
        }
    }
    /// Returns a reference to the inner of this [`DirectedCursor`].
    pub fn inner(&self) -> &Edge {
        match self {
            Self::Backwards(edge) => edge,
            Self::Forward(edge) => edge,
        }
    }

    /// Removes the direction information and returns an Edge
    pub fn into_inner(self) -> Edge {
        match self {
            Self::Backwards(edge) => edge,
            Self::Forward(edge) => edge,
        }
    }
}

impl Display for DirectedCursor {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.inner())
    }
}