Vantage Vista
Vista is the type-erased Table<T, E>. It takes a typed table, the kind a model layer defines
(bakery_model3::Product::sqlite_table(), vantage_aws::models::ecs::clusters_table()), and seals
its types behind a TableShell, so generic code (a CLI, a desktop UI, a config-driven dashboard)
can drive any backend through one handle. Rows travel as CBOR (ciborium::Value) and ids as
String, whatever the backend stores natively.
The shape is the same as Table's: Vista is Clone, owns its query state, narrows with
with_* and changes in place with add_*/set_*.
Three kinds of object
Every public type in Vantage above Table is one of three kinds, and its API follows from the
kind. A value (Table, Vista, VistaQuery, Reference) is narrowed by
clone().with_*(), which returns a new value and leaves the original alone, and changes through
add_*/set_* only when you own it (&mut self). A shared handle (diorama's Lens and
Dio) never changes after construction: with_* derives a new handle. View state (diorama's
sceneries and Servo) is opened by a builder and changed with set_* by its single owner. This
crate holds only values; the other two kinds live in vantage-diorama.
Examples
Build a Vista
A driver factory turns a typed table into a Vista:
use SqliteVistaFactory;
let products = new.from_table?;
from_table consumes the typed table and gives a Vista that knows its columns, references, id
column and the backend's capabilities. The schema is fixed from there; Vista has no
add_column.
With no Rust entity for the table, the same factory builds one from a YAML spec:
use VistaFactory;
let products = factory.from_yaml?;
A driver author wraps a shell directly:
use ;
let vista = new;
VistaFactory and TableShell are imported from the crate root, not the prelude: only driver
code and spec loaders name them.
Narrow
use *;
let pastries = products
.clone
.with_condition?
.with_order?;
let mut cheap = products.clone;
cheap.add_condition?;
cheap.set_search?;
products is untouched by both. Each clause is offered to the shell first and recorded only once
the shell accepts it, so pastries.query() is a VistaQuery holding exactly what was applied. A
clause the backend can't take is refused with an Unsupported error naming the Capability it
lacks. An unknown column is a plain error, so a typo is never mistaken for a missing feature.
A query as a value
VistaQuery is plain data: Conditions (column <op> value), a search text, an order
((column, SortDirection)) and the keys of any driver-native conditions. Build one and apply it:
let query = new
.with_condition
.with_order;
let = products.clone.with_query?;
residual holds the clauses the shell refused. A caller that needs every clause applied checks
it is empty; a cache or a client-side refine evaluates it over the rows itself, with
residual.matches(&row, &metadata) and residual.sort_rows(&mut rows, &metadata) (the evaluator
documented in vantage_vista::eval).
query.key() is a stable string for cache identity.
Read and write
Vista implements ValueSet, ReadableValueSet, WritableValueSet and InsertableValueSet,
the same trait family Table<T, E> does:
let rows = pastries.list_values.await?;
let total = pastries.count.await?;
let page = pastries.fetch_window.await?; // page.rows, page.total
The leading window (fetch_window(0, n)) works on every shell. Any other offset needs
Capability::Window; a forward-only backend offers fetch_cursor under Capability::Cursor.
A write through a narrowed Vista needs Capability::ConfineWrites, so it can't land outside the
set.
Capabilities
let caps = products.capabilities;
if caps.has
if caps.has
if caps.has
if caps.has
Each Capability names exactly one method family, and a shell that advertises one must answer it
without refusing; the contract feature checks that. A read-only AWS API and a writable SQLite
table look the same from outside; the set tells the UI which controls to draw.
Catalog and resolvers
VistaResolver is the one way to turn a name into a Vista: a model in a catalog, a relation's
target, a table a script names. Any Fn(&str) -> Result<Vista> is one. Catalog maps model names
to loaders, which may sit on different backends, and is itself a resolver:
use Catalog;
let catalog = new
.with
.with;
let orders = catalog.vista?;
A name the resolver doesn't know is not_found; a loader that fails for another reason keeps its
own error kind, so a caller can tell a missing model from an unreachable one. Within one backend,
vista.get_ref("orders", Some(&row)) traverses a declared reference; across backends, narrow the
catalog's target with reference.narrow(catalog.vista(&reference.target)?, &row).
Scripting with Rhai
The rhai feature adds the rhai module and its DataVocab, the one data vocabulary every
Vantage script uses:
let due = table("order").where("status", "due");
due.ref("client").list()
table(name) returns a handle that narrows (where, sort, search, limit, ref) and then
reads (list, get, first, count, ids), writes (insert, upsert, patch, delete,
import_from) or opens a record draft. A host picks the tables behind table(name) and which
verbs exist. The full guide is Scripting with Rhai in the
Vantage book.
Why not a stateless shell?
A shell that held no query, with every read taking a VistaQuery, would make Vista a thin
wrapper and drivers simpler. It was considered and rejected: Vista would stop matching
Table<T, E>, which owns its conditions, and code written against the dataset traits would need a
query threaded through every call. The cost of the path taken is that a driver's shell is Clone
and carries its narrowing, and a clone must not share it.
Prelude
use vantage_vista::prelude::*; brings in Vista, VistaQuery, Condition, FilterOp,
SortDirection, Capability, VistaResolver, CborValue, and the vantage-dataset traits that
Vista implements (ValueSet, ReadableValueSet, WritableValueSet, InsertableValueSet).
Everything else is reached from the crate root: VistaFactory and TableShell for driver
authors, Catalog, Column, Reference, Page, VistaMetadata. VistaCapabilities is what
vista.capabilities() returns.
Features
mocks:vantage_vista::mocks::MockShell, an in-memoryTableShellfor tests and examples. Enable it from[dev-dependencies].rhai: therhaimodule (DataVocab, the table handle, script-slot evaluators,run_script). Backends add their vendor expression vocabulary throughTableShell::register_rhai_extensions.contract:vantage_vista::contract, the driver-facing checks every backend's test suite runs against its own fixture. Enable it from[dev-dependencies].
A test Vista over MockShell:
use MockShell;
let users = new;
Integration
- vantage-dataset: the
ValueSettraitsVistaimplements - vantage-types: the
Record<V>carrier - vantage-core:
Result/VantageError - vantage-rhai: the script host behind the
rhaifeature - vantage-diorama: the
Lens/Diocache and the sceneries that sit on aVista