Skip to content

Commit 325d2b0

Browse files
committed
docs(stack-encrypt): say what is transitional and what the engine does not call
Three places where the docs promised more than the code does. An indexed plan field without "type" is still dispatched on each value's own tag, so for that field the engine trusts the binding's tagging: a 34 sent once as a Float64 and once as an Int64 under one "ore" field stores two different terms. That keeps the Go binding's current plans valid, and it is meant to end once that binding fills "type" from its struct types. The plan() rustdoc, FieldPlan and the dynamic module docs now say so, and point at #1082, which tracks making "type" required on every field with a term output. dynamic::read is the whole query-side type story, and nothing in the engine calls it: term() takes a Scalar and an IndexSpec and never sees the field's declared kind. Its rustdoc now says that plainly, that a binding deriving a query term for a typed field must call it first, and that wiring it into query(v).using(&plan) is tracked in #1057. Indexes::select said generic code over X: Indexes<S> can call indexes.select::<Ore, _>(), which does not compile without a Select bound. The sentence is replaced by a doctest that shows the bound, and the method's position parameter is renamed At -> P, so the rendered signature no longer reads as if it named the At<N> struct.
1 parent d284f62 commit 325d2b0

4 files changed

Lines changed: 55 additions & 14 deletions

File tree

‎packages/stack-encrypt/src/dynamic/kind.rs‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@
2525
//! plan is built), and refuses a value of any other kind (when it runs),
2626
//! so the term bytes are the declared kind's and no binding is trusted to
2727
//! have tagged the value right;
28-
//! - **query** reads the query value *as* the field's kind ([`read`]): `34`
29-
//! against a `uint64` field is the `u64` term, whatever number type the
30-
//! host handed over;
28+
//! - **query** is meant to read the query value *as* the field's kind
29+
//! ([`read`]): `34` against a `uint64` field is the `u64` term, whatever
30+
//! number type the host handed over. The engine does not do this for the
31+
//! caller yet; see [`read`];
3132
//! - **decrypt** refuses an opened value of any other kind, so a host with no
3233
//! types of its own can rely on the declaration for what it gets back (an
3334
//! integer, not a float; bytes, not a string).
35+
//!
36+
//! A field with no `"type"` is dispatched on each value's own tag. That is
37+
//! transitional; see [`record::plan`](super::record::plan()).
3438
use vitaminc_aead_value::{FfiValue, ValueKind};
3539

3640
use super::Error;
@@ -76,6 +80,15 @@ pub fn admits(kind: ValueKind, index: &IndexSpec) -> bool {
7680
/// being sealed is not converted; it must already be of the field's kind,
7781
/// and is refused otherwise.
7882
///
83+
/// **The engine does not call this.** [`term`](super::term()) takes a
84+
/// [`Scalar`](super::Scalar) and an [`IndexSpec`] and never sees the field's
85+
/// declared kind, so a binding deriving a query term for a typed field must
86+
/// call `read` first, with [`FieldPlan::field_type`](super::FieldPlan::field_type).
87+
/// One that skips it hands `Float64(34.0)` to a `uint64` field, derives a
88+
/// float term where the stored one is the `u64` term, and matches nothing,
89+
/// with no error. Wiring this into the plan's query path
90+
/// (`query(v).using(&plan)`) is tracked in #1057.
91+
///
7992
/// ```
8093
/// use stack_encrypt::dynamic::{read, FfiValue, ValueKind};
8194
///

‎packages/stack-encrypt/src/dynamic/mod.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
//! the type it was sealed as. They are not this crate's: a declared type is
4747
//! vitaminc's [`ValueKind`], re-exported here, whose names vitaminc freezes
4848
//! beside its tag table. This crate adds only what a kind means to an index
49-
//! ([`admits`]) and to a query value ([`read`]).
49+
//! ([`admits`]) and to a query value ([`read`]). A field without `"type"` is
50+
//! dispatched on each value's own tag; that is transitional, and
51+
//! [`record::plan`] says until when.
5052
//!
5153
//! For the same reason the enums that spell them — [`Output`],
5254
//! [`IndexSpec`] and [`ValueKind`] — are *not* `#[non_exhaustive]`, against this workspace's

‎packages/stack-encrypt/src/dynamic/record.rs‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ impl Output {
116116
/// (checked per value), so the engine verifies what a binding hands it
117117
/// rather than trusting the binding's tagging. A field with no declared type
118118
/// is dispatched on each value's own type, as every field was before types
119-
/// existed; that keeps the plans existing bindings send valid.
119+
/// existed; that keeps the plans existing bindings send valid, and is
120+
/// transitional (see [`plan`]).
120121
#[derive(Clone, Debug)]
121122
pub struct FieldPlan {
122123
name: String,
@@ -296,7 +297,16 @@ impl Plan {
296297
/// …; see [`ValueKind::name`]): vitaminc's vocabulary, not one of this
297298
/// crate's. Declared, it is checked against the field's outputs here
298299
/// ([`admits`]) and against every value sealed into or opened
299-
/// from the field; absent, each value is dispatched on its own type.
300+
/// from the field.
301+
///
302+
/// **An indexed field without `"type"` is dispatched on each value's own
303+
/// tag**, so for that field the engine trusts the binding to tag every value
304+
/// the same way: a `34` sent once as a `Float64` and once as an `Int64` under
305+
/// one `"ore"` field is accepted both times and stores two different terms.
306+
/// This is transitional. It keeps the plans the Go binding sends today, which
307+
/// carry no `"type"`, valid until that binding fills `"type"` from its struct
308+
/// types; then `"type"` becomes required on every field with a term output
309+
/// (#1082).
300310
///
301311
/// `<context>` is defined once, in [`super::context`](super::context()): a
302312
/// string, bytes, an integer, or a list of those, with what each spells in

‎packages/stack-encrypt/src/target/index.rs‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -288,13 +288,13 @@ pub trait Indexes<S> {
288288
/// query asks one index for one term and needs no ciphertext; its
289289
/// [`operation`](Index::operation) is that term alone.
290290
///
291-
/// `At` is where `I` sits and is inferred; leave it `_`. Asking for an
292-
/// index the set does not hold does not compile, and neither does asking
293-
/// for one the set holds twice, since its place is then ambiguous.
291+
/// `P` is where `I` sits (the position [`Select`] names) and is inferred;
292+
/// leave it `_`. Asking for an index the set does not hold does not
293+
/// compile, and neither does asking for one the set holds twice, since
294+
/// its place is then ambiguous.
294295
///
295-
/// In code generic over `X: Indexes<S>`, as the plan builder is, this is
296-
/// `indexes.select::<Ore, _>()`. A concrete tuple is a set of indexes
297-
/// over many plaintexts, so there the plaintext is named:
296+
/// A concrete tuple is a set of indexes over many plaintexts, so there
297+
/// the plaintext is named:
298298
///
299299
/// ```
300300
/// use stack_encrypt::target::{Equality, Index, IndexSpec, Indexes, Ore};
@@ -303,9 +303,25 @@ pub trait Indexes<S> {
303303
/// let ore = Indexes::<u32>::select::<Ore, _>(&indexes);
304304
/// assert_eq!(Index::<u32>::spec(ore), IndexSpec::Ore);
305305
/// ```
306-
fn select<I: Index<S>, At>(&self) -> &I
306+
///
307+
/// In code generic over the set, the position is a type parameter too,
308+
/// and the set must be bounded by [`Select`] for it: `X: Indexes<S>`
309+
/// alone does not let the call compile.
310+
///
311+
/// ```
312+
/// use stack_encrypt::target::{Equality, Index, IndexSpec, Indexes, Ore, Select};
313+
///
314+
/// fn ore_spec<X, P>(indexes: &X) -> IndexSpec
315+
/// where
316+
/// X: Indexes<u32> + Select<Ore, P>,
317+
/// {
318+
/// Index::<u32>::spec(indexes.select::<Ore, P>())
319+
/// }
320+
/// assert_eq!(ore_spec(&(Equality, Ore)), IndexSpec::Ore);
321+
/// ```
322+
fn select<I: Index<S>, P>(&self) -> &I
307323
where
308-
Self: Select<I, At>,
324+
Self: Select<I, P>,
309325
{
310326
Select::get(self)
311327
}

0 commit comments

Comments
 (0)