#[non_exhaustive]pub struct ReadPlan {
pub branch: Option<BranchId>,
pub valid: Option<String>,
pub recorded: Option<String>,
pub limit: Option<usize>,
}Expand description
The lineage, the two instants, and the ceiling a read is taken under.
Every field is None by default and None means the same thing on all of
them: the ordinary read. No branch is the trunk, no valid instant is
now, no recorded instant is current belief, no limit is the whole answer. A
default ReadPlan and no plan at all are the same read, which is what
lets TraversalBuilder::plan be
additive over the setters it composes rather than another way to configure a
traversal.
§Why the branch is a BranchId and the instants are String
Not an oversight, and not symmetry withheld for its own sake. A branch name
is validated at construction — length, control characters, surrounding
whitespace — and BranchId is the type that has already asked those
questions, so taking a String here would move a refusal out of the
caller’s BranchId::new and into somewhere inside a read. A timestamp has
no such type in this crate: the canonical form is enforced at the boundary
(util::timestamps) and carried as a string everywhere below it, and
inventing an instant newtype for one struct would give the crate two
answers to what a stamp is. So Self::on cannot fail and neither can
Self::valid_at; a malformed stamp is refused where every other stamp in
the crate is refused.
§Errors, when this is executed
A plan is inert and returns nothing. The refusals belong to the read that
takes one: DbError::UnknownBranch for a
lineage that was never registered, and
DbError::RecordedInstantUnreachable
for a transaction-time instant the hot log no longer answers for
(D-247).
use macrame::prelude::*;
let plan = ReadPlan::new()
.on(BranchId::new("exp")?)
.valid_at("2026-01-06T00:00:00.000000Z")
.recorded_at("2026-03-01T00:00:00.000000Z");
// The same qualifiers, on a whole-ledger read and on a walk.
let edges = db.edges(plan.clone()).await?;
let reached = TraversalBuilder::new("a")
.plan(plan)
.execute_ids(db.read_conn(), "2026-06-01T00:00:00.000000Z")
.await?;Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.branch: Option<BranchId>The lineage to read, or None for the trunk (§15.3, D-220).
valid: Option<String>The valid-time instant: what was true then. None is now.
recorded: Option<String>The transaction-time instant: what did we believe then.
None is current belief, which is a projection read rather than a fold
bounded at the present — the same answer, and only one of them is
cheap. See
TraversalBuilder::as_of_recorded.
limit: Option<usize>How much of the answer to pay for, or None for all of it (0.15.10,
W13.5, C-8).
The one field that does not narrow which rows are true. The other
three name a read; this one bounds it, so a plan carrying a limit
describes an answer that is a prefix of the read rather than the read.
What “prefix” means differs by surface and each says so:
TraversalBuilder::limit()
bounds the walk’s rows and returns the near end of the neighbourhood,
Database::edges bounds its own statement and
returns an arbitrary n of the ledger.
Implementations§
Source§impl ReadPlan
impl ReadPlan
Sourcepub fn valid_at(self, ts: impl Into<String>) -> Self
pub fn valid_at(self, ts: impl Into<String>) -> Self
Read at a valid-time instant rather than the present.
Sourcepub fn recorded_at(self, ts: impl Into<String>) -> Self
pub fn recorded_at(self, ts: impl Into<String>) -> Self
Read under the belief held at a transaction-time instant.
Sourcepub fn limit(self, n: usize) -> Self
pub fn limit(self, n: usize) -> Self
Pay for at most n rows (0.15.10, W13.5, C-8).
A ceiling on the read’s cost, not on which rows are true, and the two
surfaces that take a plan spend it differently because their statements
are different shapes. On a traversal it becomes
TraversalBuilder::limit(),
where it stops the recursion and yields the nodes nearest the start. On
Database::edges it becomes a LIMIT on one
flat projection, where the rows it keeps are whichever n the engine
reaches first — that read states no order and adding one to make the
truncation look principled would put a sort on the largest statement in
the crate.
So a limited plan is honest about being a sample, and both surfaces let
a caller tell that it was one: the traversal by
WalkOutcome, and edges by returning
exactly n, which is the ordinary convention because that statement
drops nothing after the limit applies.
The method and the field share a name, as they do nowhere else on this
struct. on/branch, valid_at/valid and recorded_at/recorded
read as sentences and limit_to/limit does not; p.limit(5) and
p.limit are unambiguous to the compiler and to a reader, and the
spelling matches
TraversalBuilder::limit(),
which is where a caller meets the idea first.
Trait Implementations§
impl Eq for ReadPlan
impl StructuralPartialEq for ReadPlan
Auto Trait Implementations§
impl Freeze for ReadPlan
impl RefUnwindSafe for ReadPlan
impl Send for ReadPlan
impl Sync for ReadPlan
impl Unpin for ReadPlan
impl UnsafeUnpin for ReadPlan
impl UnwindSafe for ReadPlan
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request