Skip to main content

Crate vantage_vista

Crate vantage_vista 

Source
Expand description

§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 vantage_sql::sqlite::vista::SqliteVistaFactory;

let products = SqliteVistaFactory::new(db.clone()).from_table(Product::sqlite_table(db))?;

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 vantage_vista::VistaFactory;

let products = factory.from_yaml(include_str!("products.yaml"))?;

A driver author wraps a shell directly:

use vantage_vista::{TableShell, Vista};

let vista = Vista::new("product", MyShell::new(conn));

VistaFactory and TableShell are imported from the crate root, not the prelude: only driver code and spec loaders name them.

§Narrow

use vantage_vista::prelude::*;

let pastries = products
    .clone()
    .with_condition("category", FilterOp::Eq, "pastries")?
    .with_order("price", SortDirection::Descending)?;

let mut cheap = products.clone();
cheap.add_condition("price", FilterOp::Lt, 5)?;
cheap.set_search(Some("tart"))?;

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 = VistaQuery::new()
    .with_condition(Condition::new("category", FilterOp::Eq, "pastries"))
    .with_order("price", SortDirection::Ascending);

let (pastries, residual) = products.clone().with_query(&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(40, 20).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(Capability::Insert) { /* show "+ New" */ }
if caps.has(Capability::Order) { /* render sortable headers */ }
if caps.has(Capability::Search) { /* render the quicksearch box */ }
if caps.has(Capability::Subscribe) { /* follow products.watch() */ }

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 vantage_vista::Catalog;

let catalog = Catalog::new()
    .with("client", |name: &str| clients_vista(name))
    .with("order", |name: &str| orders_vista(name));

let orders = catalog.vista("order")?;

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, offset, 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-memory TableShell for tests and examples. Enable it from [dev-dependencies].
  • rhai: the rhai module (DataVocab, the table handle, script-slot evaluators, run_script). Backends add their vendor expression vocabulary through TableShell::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 vantage_vista::mocks::MockShell;

let users = Vista::new("user", MockShell::new().with_record("u1", record));

§Integration

  • vantage-dataset: the ValueSet traits Vista implements
  • vantage-types: the Record<V> carrier
  • vantage-core: Result / VantageError
  • vantage-rhai: the script host behind the rhai feature
  • vantage-diorama: the Lens/Dio cache and the sceneries that sit on a Vista

Modules§

eval
Evaluating a VistaQuery over rows held in memory.
prelude
What nearly every user of vantage-vista writes.

Macros§

forward_table_shell
Implement TableShell for $ty, whose $field holds the inner shell (anything that derefs to a TableShell, usually a Box<dyn TableShell>). $ty must be Clone; its clone carries the inner shell’s clone, narrowing included.

Structs§

Catalog
A map from model name to the loader that builds it.
Column
Display-relevant column metadata held by a Vista.
ColumnSpec
Per-column metadata in a VistaSpec.
Committed
What a successful commit reports.
Condition
column <op> value. Equality is FilterOp::Eq.
ContainedYaml
YAML shape of a contained relation — embedded records stored in a column of the parent row. Mirrors ReferenceSpec but carries the contained record’s own column schema (built via the driver’s build_column) instead of a foreign key.
JoinKey
One condition a join contributes: read the parent row’s from column and constrain the target’s to column to that value.
NativeCondition
A condition in a driver’s own type (a SurrealDB expression, a BSON document), passed through vista unread, with a key naming it.
NoExtras
Empty extras placeholder. Serializes as an absent key.
Page
One window of rows, in the set’s order, and the set’s total row count when the fetch learned it. None means the fetch didn’t say, never zero.
Preview
The query a shell would send for its next read, rendered without sending it. A debugging surface: it must not show a clause the shell wouldn’t send.
Reference
A relation a Vista exposes: a join to another table, or records contained in a column of this table’s rows. Traversed with Vista::get_ref.
ReferenceSpec
Top-level reference declaration in a VistaSpec.
ShortId
A short random id: a fixed length over a fixed alphabet.
Transaction
A unit of work shared by every backend it touches. Commit runs backend by backend, so it’s atomic per backend, not across them: if one fails part-way, the backends before it stay committed and the rest roll back.
TxKey
Identifies a participant within one transaction by the address of a token the caller keeps alive (typically the connection it belongs to).
TxOpen
The begun transaction’s handle, locked. A plain MutexGuard, not a mapped one: tokio’s MappedMutexGuard is Send only under a lifetime bound the compiler can’t prove inside the async fns that hold it.
TxSlot
Vista
Universal, schema-bearing data handle: the type-erased Table<T, E>.
VistaCapabilities
The set of capabilities a shell advertises.
VistaMetadata
Schema a Vista is built around.
VistaQuery
Every clause applied through a Vista: its conditions, search and order, plus the keys of its driver-native conditions (NativeCondition::key). A native condition itself is opaque, so only its key is recorded, and a VistaQuery that holds native keys can’t be re-applied to another Vista.
VistaSpec
The YAML schema a vista is built from.
WeakTransaction
A Transaction handle that doesn’t keep it alive.

Enums§

Capability
One method family of TableShell that a shell may or may not implement.
ColumnFamily
The family of a column’s declared type.
ColumnFlag
A flag on a Column.
FilterOp
A comparison operator. The scalar operators mirror the query-builder vocabulary the SQL and SurrealDB expression layers already expose (eq/ne/gt/…); InSet / NotInSet test membership against a list and carry a ciborium::Value::Array operand rather than a scalar.
IdGenerator
How a missing id is minted when a new record is inserted.
ReferenceKind
What a relation points at, and how many records it yields.
ReferenceSugar
Sugar form for inline column references.
SortDirection
VistaChange
A single change observed on the underlying set by a live subscription.

Traits§

CloneTableShell
Boxed cloning for TableShell, so Box<dyn TableShell> is Clone. Every TableShell + Clone has it; a shell implements Clone and gets this for free. The trait is sealed: a clone must come from Clone, so it carries the shell’s narrowing.
SpecResolver
Looks up a spec by vista name. Factories hold it as Arc<dyn SpecResolver<S>> and hand clones into each reference, so a child table is rebuilt from the current spec at traversal time. Any Fn(&str) -> Option<S> closure is a resolver.
TableShell
The driver side of a Vista: schema, reads, narrowing and writes over one table of one backend.
TxBackend
What a TxSlot needs from a backend. Errors come back already worded (couldn't begin the transaction, transaction commit failed, …).
TxParticipant
One backend’s share of a transaction, finished together with the others.
VistaFactory
Factory that produces Vista instances from configuration sources.
VistaResolver
Builds the Vista a name stands for: a model in a Catalog, a relation’s bare target, a table a script names.

Functions§

build_contained_vista
Build a sub-Vista over the records reference embeds in host_value.
cant_take_part
The refusal of a table whose shell can’t rebind to a transaction. Marked Unsupported: Transaction::wrap renames any Unsupported rebind refusal to the wrapped Vista’s name.
condition_column_error
The error for a condition on column, which the shell doesn’t take as one of its own columns.
direct
shell, taking part in a transaction by writing outside it: a write through the wrapped Vista lands at once, stays after a rollback, and is reported in Committed::unprotected.
operand_text
A FilterOp::Like operand as the text it matches against — the one spelling every side of the comparison uses, so a pattern pushed into a query and the same pattern evaluated locally read the operand alike.
resolve_base_spec
Resolve the spec a base: vista derives from. Errors when no resolver is attached or the base isn’t known to it.
unknown_relation
The error for a relation a shell doesn’t declare, marked not_found so a caller can tell a missing relation from one that fails to traverse.
unsupported
The refusal of a method whose capability the shell doesn’t have: an Unsupported error naming the capability. Default trait methods answer with it; a caller probing a capability reads it as “no”, not as a fault.

Type Aliases§

CborValue
Convenience alias for the carrier type used at the TableShell boundary.
ContainedWriteback
Persists a contained relation’s collection back into the parent row.
VistaChangeStream
A stream of VistaChanges from a live subscription. 'static and Send so it can be handed to a background task.