Skip to main content

vantage_vista/
source.rs

1use std::pin::Pin;
2
3use async_trait::async_trait;
4use ciborium::Value as CborValue;
5use futures_core::Stream;
6use indexmap::IndexMap;
7use vantage_core::{Result, VantageError, error};
8use vantage_types::Record;
9
10use crate::{capabilities::VistaCapabilities, vista::Vista};
11
12/// Per-driver executor for a `Vista`.
13///
14/// Implementations live in driver crates (vantage-sqlite, vantage-mongodb,
15/// vantage-aws, etc.). Each method receives `&Vista` so the driver can read
16/// the current condition state, columns, and other metadata.
17///
18/// `Id = String` and `Value = ciborium::Value` at this boundary, mirroring
19/// `AnyTable`. CBOR-typed driver ids (Mongo `ObjectId`, Surreal `Thing`)
20/// stringify here. Methods are named with the `_vista_` infix to mirror
21/// `TableSource`'s `_table_` convention; `Vista`'s `ValueSet` impls
22/// delegate by stripping the infix.
23///
24/// `id: &String` (rather than `&str`) is intentional: the upstream
25/// `vantage_dataset::ValueSet` trait family fixes `Id = String` and uses
26/// `&Self::Id` in its signatures, so impls receive `&String` and forward
27/// it through unchanged.
28#[async_trait]
29#[allow(clippy::ptr_arg)]
30pub trait TableShell: Send + Sync + 'static {
31    // ---- ReadableValueSet delegates ----------------------------------------
32
33    async fn list_vista_values(&self, vista: &Vista)
34    -> Result<IndexMap<String, Record<CborValue>>>;
35
36    async fn get_vista_value(
37        &self,
38        vista: &Vista,
39        id: &String,
40    ) -> Result<Option<Record<CborValue>>>;
41
42    async fn get_vista_some_value(
43        &self,
44        vista: &Vista,
45    ) -> Result<Option<(String, Record<CborValue>)>>;
46
47    /// Default implementation wraps `list_vista_values`. Drivers with native
48    /// streaming (cursor-based queries, paginated REST APIs) override.
49    #[allow(clippy::type_complexity)]
50    fn stream_vista_values<'a>(
51        &'a self,
52        vista: &'a Vista,
53    ) -> Pin<Box<dyn Stream<Item = Result<(String, Record<CborValue>)>> + Send + 'a>>
54    where
55        Self: Sync,
56    {
57        Box::pin(async_stream::stream! {
58            match self.list_vista_values(vista).await {
59                Ok(map) => {
60                    for item in map {
61                        yield Ok(item);
62                    }
63                }
64                Err(e) => yield Err(e),
65            }
66        })
67    }
68
69    // ---- WritableValueSet delegates ----------------------------------------
70    //
71    // Default impls return a typed VantageError via `default_error` — drivers
72    // override only what they actually support. The matching `VistaCapabilities`
73    // flag must be set to `true` for any method the driver implements; if the
74    // flag is `true` but the trait method falls through to the default,
75    // `default_error` produces an `Unimplemented`-kind error (placeholder
76    // detected). If the flag is `false`, it produces `Unsupported`. Both are
77    // emitted as tracing events at construction.
78
79    async fn insert_vista_value(
80        &self,
81        vista: &Vista,
82        _id: &String,
83        _record: &Record<CborValue>,
84    ) -> Result<Record<CborValue>> {
85        Err(self.default_error("insert_vista_value", "can_insert", vista))
86    }
87
88    async fn replace_vista_value(
89        &self,
90        vista: &Vista,
91        _id: &String,
92        _record: &Record<CborValue>,
93    ) -> Result<Record<CborValue>> {
94        Err(self.default_error("replace_vista_value", "can_update", vista))
95    }
96
97    async fn patch_vista_value(
98        &self,
99        vista: &Vista,
100        _id: &String,
101        _partial: &Record<CborValue>,
102    ) -> Result<Record<CborValue>> {
103        Err(self.default_error("patch_vista_value", "can_update", vista))
104    }
105
106    async fn delete_vista_value(&self, vista: &Vista, _id: &String) -> Result<()> {
107        Err(self.default_error("delete_vista_value", "can_delete", vista))
108    }
109
110    async fn delete_vista_all_values(&self, vista: &Vista) -> Result<()> {
111        Err(self.default_error("delete_vista_all_values", "can_delete", vista))
112    }
113
114    // ---- InsertableValueSet delegate ---------------------------------------
115
116    async fn insert_vista_return_id_value(
117        &self,
118        vista: &Vista,
119        _record: &Record<CborValue>,
120    ) -> Result<String> {
121        Err(self.default_error("insert_vista_return_id_value", "can_insert", vista))
122    }
123
124    // ---- Aggregates --------------------------------------------------------
125
126    /// Default impl falls back to `list_vista_values` — drivers with native
127    /// count (`SELECT COUNT(*)`, etc.) override.
128    async fn get_vista_count(&self, vista: &Vista) -> Result<i64> {
129        Ok(self.list_vista_values(vista).await?.len() as i64)
130    }
131
132    // ---- Conditions --------------------------------------------------------
133
134    /// Translate `field == value` into the driver's native condition type and
135    /// apply it to the wrapped table. The default impl returns `Unimplemented`
136    /// — every driver is expected to override.
137    ///
138    /// `value` is the universal CBOR carrier; the driver picks the appropriate
139    /// translation (e.g. `cbor_to_bson` for Mongo, `cbor → AnyCsvType` for CSV).
140    fn add_eq_condition(&mut self, _field: &str, _value: &CborValue) -> Result<()> {
141        Err(error!(
142            format!(
143                "add_eq_condition not implemented for '{}'",
144                std::any::type_name::<Self>()
145            ),
146            method = "add_eq_condition",
147            source_type = std::any::type_name::<Self>()
148        )
149        .is_unimplemented())
150    }
151
152    // ---- References --------------------------------------------------------
153
154    /// Resolve a named relation and return the related table as a new
155    /// `Vista`. The default impl returns `Unimplemented` so existing
156    /// drivers compile until they opt in; drivers that wrap a typed
157    /// `Table<T, E>` can delegate to `Table::get_ref`.
158    fn get_ref(&self, relation: &str) -> Result<Vista> {
159        Err(error!(
160            format!(
161                "get_ref not implemented for '{}'",
162                std::any::type_name::<Self>()
163            ),
164            method = "get_ref",
165            relation = relation,
166            source_type = std::any::type_name::<Self>()
167        )
168        .is_unimplemented())
169    }
170
171    // ---- Identity ----------------------------------------------------------
172
173    /// Short human label for the underlying driver (e.g. `"csv"`, `"sqlite"`,
174    /// `"postgres"`, `"mongodb"`). Used for diagnostics and CLI output.
175    /// Drivers should override; the default is a placeholder.
176    fn driver_name(&self) -> &'static str {
177        "unknown"
178    }
179
180    // ---- Capability advertisement -----------------------------------------
181
182    fn capabilities(&self) -> &VistaCapabilities;
183
184    /// Look up a capability flag by name. Used by `default_error` to decide
185    /// between `Unsupported` and `Unimplemented`. Drivers don't normally
186    /// need to override this.
187    fn capability_flag(&self, name: &str) -> bool {
188        let caps = self.capabilities();
189        match name {
190            "can_count" => caps.can_count,
191            "can_insert" => caps.can_insert,
192            "can_update" => caps.can_update,
193            "can_delete" => caps.can_delete,
194            "can_subscribe" => caps.can_subscribe,
195            "can_invalidate" => caps.can_invalidate,
196            _ => false,
197        }
198    }
199
200    /// Build the standard error returned by default trait method impls.
201    ///
202    /// Picks the kind based on the capability flag: a `true` flag means the
203    /// driver advertised support but didn't override the method (placeholder
204    /// → `Unimplemented`); a `false` flag means the driver honestly doesn't
205    /// claim the op (caller should have checked → `Unsupported`).
206    ///
207    /// Both kinds emit a `tracing::error!` at construction with `method`,
208    /// `capability`, `source_type`, and `vista_name` as structured fields.
209    fn default_error(&self, method: &str, capability: &str, vista: &Vista) -> VantageError {
210        let source_type = std::any::type_name::<Self>();
211        let vista_name = vista.name().to_string();
212        if self.capability_flag(capability) {
213            error!(
214                format!(
215                    "'{}' is advertised as VistaCapability for '{}' but implementation for '{}' is missing",
216                    capability, source_type, method
217                ),
218                method = method,
219                capability = capability,
220                source_type = source_type,
221                vista_name = vista_name
222            )
223            .is_unimplemented()
224        } else {
225            error!(
226                format!(
227                    "'{}' is not supported by '{}'; '{}' refused",
228                    capability, source_type, method
229                ),
230                method = method,
231                capability = capability,
232                source_type = source_type,
233                vista_name = vista_name
234            )
235            .is_unsupported()
236        }
237    }
238}