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}