Skip to main content

vantage_vista/
page.rs

1//! What reads return beyond plain rows: [`Page`], [`Preview`] and the opaque
2//! [`NativeCondition`] a caller hands to a shell.
3
4use std::any::Any;
5use std::fmt;
6
7use ciborium::Value as CborValue;
8use serde::Serialize;
9use vantage_types::Record;
10
11mod listing;
12
13/// One window of rows, in the set's order, and the set's total row count
14/// when the fetch learned it. `None` means the fetch didn't say, never zero.
15#[derive(Clone, Debug, Default, PartialEq)]
16#[non_exhaustive]
17pub struct Page {
18    pub rows: Vec<(String, Record<CborValue>)>,
19    pub total: Option<i64>,
20}
21
22impl Page {
23    pub fn new(rows: Vec<(String, Record<CborValue>)>) -> Self {
24        Self { rows, total: None }
25    }
26
27    pub fn with_total(mut self, total: i64) -> Self {
28        self.total = Some(total);
29        self
30    }
31}
32
33/// The query a shell would send for its next read, rendered without sending
34/// it. A debugging surface: it must not show a clause the shell wouldn't
35/// send.
36///
37/// The driver isn't part of it: read
38/// [`Vista::driver_name`](crate::Vista::driver_name).
39#[derive(Clone, Debug, PartialEq, Serialize)]
40#[non_exhaustive]
41pub struct Preview {
42    /// What the backend would run (SQL, a GraphQL document, a Mongo filter,
43    /// an argv), including every condition the shell pushes to it; `None`
44    /// when nothing is queried.
45    pub query: Option<String>,
46    /// Only what `query` leaves out of the read (conditions evaluated
47    /// locally, client-side search), or why nothing is queried when `query`
48    /// is `None`. Never a restatement of the query.
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub note: Option<String>,
51}
52
53impl Preview {
54    /// No query and no note. The one way to start a preview: `Preview` has
55    /// no `Default`.
56    #[allow(clippy::new_without_default)]
57    pub fn new() -> Self {
58        Self {
59            query: None,
60            note: None,
61        }
62    }
63
64    pub fn with_query(mut self, query: impl Into<String>) -> Self {
65        self.query = Some(query.into());
66        self
67    }
68
69    pub fn with_note(mut self, note: impl Into<String>) -> Self {
70        self.note = Some(note.into());
71        self
72    }
73}
74
75/// A condition in a driver's own type (a SurrealDB expression, a BSON
76/// document), passed through vista unread, with a `key` naming it.
77///
78/// The key is what [`VistaQuery`](crate::VistaQuery) records for the
79/// condition and what [`VistaQuery::key`](crate::VistaQuery::key) includes,
80/// so two Vistas narrowed by different native conditions get different cache
81/// identities. Give equal conditions equal keys, typically the rendered
82/// expression.
83///
84/// The driver downcasts the condition to the type it takes. A condition of
85/// another type is a caller mistake, answered with an `IncorrectUsage`
86/// error, not `Unsupported`.
87pub struct NativeCondition {
88    condition: Box<dyn Any + Send + Sync>,
89    key: String,
90}
91
92impl NativeCondition {
93    pub fn new<C: Any + Send + Sync>(condition: C, key: impl Into<String>) -> Self {
94        Self {
95            condition: Box::new(condition),
96            key: key.into(),
97        }
98    }
99
100    pub fn key(&self) -> &str {
101        &self.key
102    }
103
104    /// The condition as `C`, or `self` back when it holds another type.
105    pub fn downcast<C: Any>(self) -> Result<C, Self> {
106        let Self { condition, key } = self;
107        condition
108            .downcast::<C>()
109            .map(|c| *c)
110            .map_err(|condition| Self { condition, key })
111    }
112}
113
114impl fmt::Debug for NativeCondition {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        f.debug_tuple("NativeCondition").field(&self.key).finish()
117    }
118}