vitaminc_normalize/terms.rs
1//! Equality terms as a sink, and encryption that produces them in the
2//! same walk.
3//!
4//! [`Terms`] is a [`Sink`] that produces one [`Term`] per scalar that has
5//! an equality domain. It runs beside encryption as `Tee(EncryptSink,
6//! Terms)`, so a caller holds every term before any cipher is called and,
7//! with an async cipher, before the first await. [`terms`] runs it alone,
8//! and then it refuses what encryption would refuse.
9//!
10//! The sink does the walking and the [`Normalize`] it is given does the
11//! encoding, so an index of another kind (an order index, whose bytes
12//! differ from an equality term's) collects its terms through the same
13//! sink with a normalizer of its own, through [`Terms::by`] and
14//! [`terms_by`].
15
16use core::future::Future;
17use core::marker::PhantomData;
18
19use vitaminc_aead::plaintext::{Error, Path, Step};
20use vitaminc_aead::seal::{seal_all, seal_all_async, AsyncCipher, Cipher};
21use vitaminc_aead::shape::{self, Locate, MapRules, SeqRules, Settle};
22use vitaminc_aead::{
23 Encrypt, EncryptError, EncryptSink, IntoAad, Logical, MapSink, Node, Pending, Physical,
24 Refusal, SeqSink, Sink, SourceError, Tee,
25};
26
27use vitaminc_protected::OpaqueDebug;
28
29use crate::scalar::{normalize, NormalizeError, Normalized};
30
31/// How [`Terms`] turns one scalar into a term.
32///
33/// [`Equality`] is the normalizer this crate is about. An index of another
34/// kind supplies its own, with a term type of its own (an order index
35/// seals its term so only a scheme can read it) and the same outcome
36/// contract: a scalar whose kind has no term under this index (a null, a
37/// boolean) returns [`NormalizeError::NoDomain`], and [`Terms`] skips it
38/// when it has no logical type; a scalar whose logical type has no term
39/// returns the same error and stops the walk; any other error stops the
40/// walk too.
41///
42/// A normalizer is copied into every child sink of a walk, so it is
43/// `Copy`: it carries settings, such as the type an index declares, but
44/// no state that a walk changes.
45pub trait Normalize: Copy {
46 /// What one scalar becomes.
47 type Term;
48
49 /// The kind of index the terms are for, as a refusal names it:
50 /// `"equality"` for [`Equality`], so a scalar it cannot normalize is
51 /// reported as having no equality term.
52 const INDEX: &'static str;
53
54 /// The term for one scalar, or why it has none.
55 fn normalize(
56 &self,
57 ty: Option<Logical>,
58 value: Physical<'_>,
59 ) -> Result<Self::Term, NormalizeError>;
60}
61
62/// The equality normalizer: [`normalize`].
63#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
64pub struct Equality;
65
66impl Normalize for Equality {
67 type Term = Normalized;
68
69 const INDEX: &'static str = "equality";
70
71 fn normalize(
72 &self,
73 ty: Option<Logical>,
74 value: Physical<'_>,
75 ) -> Result<Normalized, NormalizeError> {
76 normalize(ty, value)
77 }
78}
79
80/// One term: where the scalar sits in the value, and what the normalizer
81/// made of it. An equality term, a [`Normalized`], unless [`Terms::by`]
82/// was given another normalizer.
83///
84/// `Debug` shows the path's shape, with its map keys masked as [`Step`]
85/// masks them, and masks the term.
86#[derive(OpaqueDebug)]
87pub struct Term<T = Normalized> {
88 /// The path from the root, empty for a scalar at the root.
89 #[non_sensitive]
90 pub path: Path,
91 /// The term itself: for equality, the frozen domain label and the
92 /// normalized bytes.
93 pub normalized: T,
94}
95
96/// A scalar [`Terms`] refused: why, and where in the value.
97///
98/// This is the type to downcast a refusal to. Its
99/// [`source`](core::error::Error::source) is the [`NormalizeError`], so a
100/// reporter that walks the error chain reaches the reason too.
101///
102/// `Display` shows only the reason, because the path can hold map keys and
103/// map keys can be user data. `Debug` shows the reason and the path's
104/// shape, with every key masked. Read the path with
105/// [`path`](TermError::path).
106#[derive(Clone, OpaqueDebug, thiserror::Error)]
107#[error("no {index} term: {error}")]
108pub struct TermError {
109 #[non_sensitive]
110 index: &'static str,
111 #[non_sensitive]
112 path: Path,
113 #[non_sensitive]
114 #[source]
115 error: NormalizeError,
116}
117
118impl TermError {
119 /// The kind of index the scalar has no term for: the normalizer's
120 /// [`Normalize::INDEX`].
121 pub fn index(&self) -> &'static str {
122 self.index
123 }
124
125 /// Where the refused scalar sits, from the root.
126 pub fn path(&self) -> &Path {
127 &self.path
128 }
129
130 /// Why the scalar has no term.
131 pub fn error(&self) -> NormalizeError {
132 self.error
133 }
134}
135
136/// What [`terms`] fails with: what [`Terms`] refuses, or the source's own
137/// failure.
138///
139/// A source reports through [`Error::source`] whichever sink it feeds, so
140/// every failure arrives as a [`SourceError`]. [`term`](TermsError::term)
141/// recovers a [`TermError`] from it, the same downcast a caller makes on
142/// [`EncryptError::Source`] when `Terms` runs beside encryption, and
143/// [`refusal`](TermsError::refusal) a value refused for its shape, which
144/// encryption reports as [`EncryptError::Shape`].
145/// [`source_error`](TermsError::source_error) is the error as it arrived.
146#[derive(Debug, thiserror::Error)]
147#[error(transparent)]
148pub struct TermsError(SourceError);
149
150impl TermsError {
151 /// The scalar [`Terms`] refused, or `None` if the source failed on its
152 /// own or the value was refused for its shape.
153 pub fn term(&self) -> Option<&TermError> {
154 self.0.inner().downcast_ref()
155 }
156
157 /// The value refused for its shape, as encryption would refuse it, or
158 /// `None` if it was refused for another reason.
159 pub fn refusal(&self) -> Option<&Refusal> {
160 self.0.inner().downcast_ref()
161 }
162
163 /// The failure as the walk reported it: what [`Terms`] refused, or the
164 /// source's own error.
165 pub fn source_error(&self) -> &SourceError {
166 &self.0
167 }
168}
169
170impl Error for TermsError {
171 fn source(error: SourceError) -> Self {
172 Self(error)
173 }
174}
175
176/// A sink that collects one [`Term`] per scalar with an equality domain,
177/// in walk order.
178///
179/// Generic over its error type so it adopts its [`Tee`] partner's. To
180/// collect terms without encrypting, call [`terms`], whose error type is
181/// [`TermsError`]. Generic too over the [`Normalize`] that encodes each
182/// scalar, [`Equality`] unless [`by`](Terms::by) is given another, so an
183/// index of another kind collects its terms through this same walk.
184///
185/// A scalar whose *kind* has no domain (a null, a boolean, an `undefined`)
186/// produces no term and does not stop the walk: an absent or unindexable
187/// value is an ordinary thing to find in a document, and the caller has
188/// the paths to see which fields were indexed. A scalar whose *logical
189/// type* has no domain ([`Logical::Unknown`], or a type this crate does
190/// not know) stops the walk instead: it came from a newer client and
191/// means something, and encrypting it with no term and no error would
192/// leave an index silently incomplete.
193///
194/// Passthrough subtrees travel in the clear and get no terms.
195///
196/// # Only what can be encrypted
197///
198/// A `Terms` refuses every value [`EncryptSink`] refuses, for the same
199/// [`Unencryptable`](vitaminc_aead::Unencryptable) reason and at the same path, so an index never holds
200/// terms for a value that cannot be stored. It keeps the
201/// [`shape`](vitaminc_aead::shape) rules through the code `EncryptSink`
202/// keeps them with: nothing deeper than the transport's depth limit, every
203/// scalar valid for its logical type, every passthrough subtree framable,
204/// no map key given twice, no child sink dropped without a value, and
205/// something sealed in every container with children. The last rule is
206/// about sealing, and this walk seals nothing, but a value that breaks it
207/// cannot be encrypted, so it is refused here too. A passthrough is framed
208/// as encryption frames it, to check that it can be, and the frame is
209/// dropped.
210///
211/// So [`terms`] accepts a value exactly when [`encrypt_with_terms`] does
212/// with a cipher that does not fail, and refuses it for the same reason at
213/// the same path. [`plan_with_terms`], and the encrypting functions built
214/// on it, pair `EncryptSink` with a `Terms` that leaves the shape rules to
215/// it, so they are kept once.
216///
217/// # Errors
218///
219/// The walk stops with `E::source(SourceError::new(error))`, where `error`
220/// is one of:
221///
222/// - a [`Refusal`]: the value breaks a shape rule, as above. A scalar not
223/// valid for its logical type is refused this way, as
224/// [`Unencryptable::InvalidValue`](vitaminc_aead::Unencryptable::InvalidValue), before it is normalized. Through
225/// [`terms`] it is [`TermsError::refusal`]. Beside `EncryptSink`, which
226/// refuses it first, it is [`EncryptError::Shape`].
227/// - a [`TermError`] holding the scalar's path and the
228/// [`NormalizeError`]: the scalar's logical type has no domain
229/// (`NoDomain`), or its text holds a code point unassigned in Unicode 16
230/// (`UnassignedCodePoint`). Beside [`EncryptSink`] that arrives as
231/// [`EncryptError::Source`], and
232/// `source.inner().downcast_ref::<TermError>()` recovers it. Alone,
233/// through [`terms`], it is [`TermsError::term`]. Either way the
234/// [`NormalizeError`] is on the `source()` chain.
235///
236/// # Cost
237///
238/// A path costs nothing until a term is pushed or a refusal is built. Each
239/// child sink holds a link to its container's place and its own index, or
240/// the position of its key in one buffer of the keys on the path being
241/// walked, which the whole walk shares and reuses. So the walk allocates
242/// that buffer, as long as the keys on the longest path, and otherwise
243/// only what each pushed term needs.
244///
245/// The shape rules cost what they cost encryption: a map keeps one copy
246/// of each of its keys until it ends, which is what checking for a repeat
247/// exactly takes, and a passthrough is framed. Beside `EncryptSink`,
248/// through [`plan_with_terms`], none of that is paid twice. A caller that
249/// tees `Terms::new` beside `EncryptSink` itself has both keep the rules.
250pub struct Terms<'t, E, N: Normalize = Equality> {
251 out: &'t mut Vec<Term<N::Term>>,
252 keys: Keys<'t>,
253 at: Place<'t>,
254 /// `None` beside an [`EncryptSink`] that keeps the rules itself.
255 rules: Option<NodeRules<'t>>,
256 by: N,
257 _error: PhantomData<E>,
258}
259
260/// The shape rules one node keeps: how deep it sits, and its hold on its
261/// container's rules, which it settles when it ends. The root has no
262/// container.
263struct NodeRules<'t> {
264 depth: usize,
265 settle: Option<Settle<'t>>,
266}
267
268impl NodeRules<'_> {
269 /// The node put a sealed node: a scalar, or a container.
270 fn sealed(self) {
271 if let Some(settle) = self.settle {
272 settle.sealed();
273 }
274 }
275}
276
277/// A container's shape rules, `R`, with the rules of the node it is.
278struct Held<'t, R> {
279 node: NodeRules<'t>,
280 rules: R,
281}
282
283/// The map keys on the path being walked, end to end. The root owns the
284/// buffer and every sink below it borrows it. A container truncates it
285/// back to where its own keys begin before each child, so it holds only
286/// the keys of the current path.
287enum Keys<'t> {
288 Own(String),
289 Lent(&'t mut String),
290}
291
292impl Keys<'_> {
293 fn get(&mut self) -> &mut String {
294 match self {
295 Keys::Own(keys) => keys,
296 Keys::Lent(keys) => keys,
297 }
298 }
299
300 fn as_str(&self) -> &str {
301 match self {
302 Keys::Own(keys) => keys,
303 Keys::Lent(keys) => keys,
304 }
305 }
306}
307
308/// Where a node sits: a link to its container's place and its own step.
309/// A key is named by its position in [`Keys`].
310#[derive(Clone, Copy)]
311enum Place<'t> {
312 Root,
313 Element {
314 parent: &'t Place<'t>,
315 index: usize,
316 },
317 Entry {
318 parent: &'t Place<'t>,
319 key: (usize, usize),
320 },
321}
322
323impl Place<'_> {
324 /// Append the steps from the root to this node, reading keys from
325 /// `keys`.
326 fn locate(&self, keys: &str, steps: &mut Vec<Step>) {
327 match *self {
328 Place::Root => {}
329 Place::Element { parent, index } => {
330 parent.locate(keys, steps);
331 steps.push(Step::Index(index));
332 }
333 Place::Entry {
334 parent,
335 key: (start, end),
336 } => {
337 parent.locate(keys, steps);
338 steps.extend(keys.get(start..end).map(|key| Step::Key(key.to_owned())));
339 }
340 }
341 }
342
343 /// The path from the root to this node.
344 fn path(&self, keys: &str) -> Path {
345 let mut steps = Vec::new();
346 self.locate(keys, &mut steps);
347 Path::new(steps)
348 }
349}
350
351/// A place with the keys it names: what the shape rules locate a refusal
352/// with.
353struct At<'a> {
354 place: &'a Place<'a>,
355 keys: &'a str,
356}
357
358impl Locate for At<'_> {
359 fn locate(&self, steps: &mut Vec<Step>) {
360 self.place.locate(self.keys, steps);
361 }
362}
363
364/// A shape refusal, reported the only way a sink generic over its error
365/// can report anything.
366fn refused<E: Error>(refusal: Refusal) -> E {
367 E::source(SourceError::new(refusal))
368}
369
370impl<'t, E> Terms<'t, E> {
371 /// A sink that appends equality terms to `out` and keeps the shape
372 /// rules itself.
373 pub fn new(out: &'t mut Vec<Term>) -> Self {
374 Self::by(Equality, out)
375 }
376
377 /// A sink that appends to `out` and leaves the shape rules to the
378 /// [`EncryptSink`] it is teed beside, which refuses first.
379 fn beside_encryption(out: &'t mut Vec<Term>) -> Self {
380 Self {
381 rules: None,
382 ..Self::new(out)
383 }
384 }
385}
386
387impl<'t, E, N: Normalize> Terms<'t, E, N> {
388 /// A sink that appends the terms `by` makes to `out` and keeps the
389 /// shape rules itself.
390 ///
391 /// The normalizer is copied into every child sink, which is why
392 /// [`Normalize`] requires `Copy`.
393 pub fn by(by: N, out: &'t mut Vec<Term<N::Term>>) -> Self {
394 Self {
395 out,
396 keys: Keys::Own(String::new()),
397 at: Place::Root,
398 rules: Some(NodeRules {
399 depth: 0,
400 settle: None,
401 }),
402 by,
403 _error: PhantomData,
404 }
405 }
406
407 fn at(&self) -> At<'_> {
408 At {
409 place: &self.at,
410 keys: self.keys.as_str(),
411 }
412 }
413}
414
415impl<'t, E: Error, N: Normalize> Sink for Terms<'t, E, N> {
416 type Ok = ();
417 type Error = E;
418 type Seq = TermsSeq<'t, E, N>;
419 type Map = TermsMap<'t, E, N>;
420
421 fn scalar(self, ty: Option<Logical>, value: Physical<'_>) -> Result<(), E> {
422 if let Some(rules) = &self.rules {
423 shape::scalar(rules.depth, ty, &value, &self.at()).map_err(refused)?;
424 }
425 match self.by.normalize(ty, value) {
426 Ok(normalized) => {
427 let path = self.at.path(self.keys.as_str());
428 self.out.push(Term { path, normalized });
429 }
430 // No domain for the kind: nothing to index, carry on. No domain
431 // for a logical type: refuse, see the type's docs.
432 Err(NormalizeError::NoDomain) if matches!(ty, None | Some(Logical::Undefined)) => {}
433 Err(error) => {
434 return Err(E::source(SourceError::new(TermError {
435 index: N::INDEX,
436 path: self.at.path(self.keys.as_str()),
437 error,
438 })))
439 }
440 }
441 if let Some(rules) = self.rules {
442 rules.sealed();
443 }
444 Ok(())
445 }
446
447 fn seq(self, _: Option<usize>) -> Result<Self::Seq, E> {
448 let rules = match self.rules {
449 Some(node) => {
450 shape::container(
451 node.depth,
452 &At {
453 place: &self.at,
454 keys: self.keys.as_str(),
455 },
456 )
457 .map_err(refused)?;
458 Some(Held {
459 node,
460 rules: SeqRules::new(),
461 })
462 }
463 None => None,
464 };
465 Ok(TermsSeq {
466 mark: self.keys.as_str().len(),
467 out: self.out,
468 keys: self.keys,
469 at: self.at,
470 next: 0,
471 rules,
472 by: self.by,
473 _error: PhantomData,
474 })
475 }
476
477 fn map(self, _: Option<usize>) -> Result<Self::Map, E> {
478 let rules = match self.rules {
479 Some(node) => {
480 shape::container(
481 node.depth,
482 &At {
483 place: &self.at,
484 keys: self.keys.as_str(),
485 },
486 )
487 .map_err(refused)?;
488 Some(Held {
489 node,
490 rules: MapRules::new(),
491 })
492 }
493 None => None,
494 };
495 Ok(TermsMap {
496 mark: self.keys.as_str().len(),
497 out: self.out,
498 keys: self.keys,
499 at: self.at,
500 rules,
501 by: self.by,
502 _error: PhantomData,
503 })
504 }
505
506 /// No terms. Keeping the shape rules, the subtree is framed, as
507 /// encryption frames it, and the frame dropped.
508 fn passthrough<P: Encrypt + Clone>(self, value: P) -> Result<(), E> {
509 if let Some(rules) = self.rules {
510 let at = At {
511 place: &self.at,
512 keys: self.keys.as_str(),
513 };
514 shape::frame_passthrough(value, rules.depth, &at, refused)?;
515 if let Some(settle) = rules.settle {
516 settle.passthrough();
517 }
518 }
519 Ok(())
520 }
521}
522
523/// The sequence side of [`Terms`]: each element's path is the sequence's
524/// plus its index.
525pub struct TermsSeq<'t, E, N: Normalize = Equality> {
526 out: &'t mut Vec<Term<N::Term>>,
527 keys: Keys<'t>,
528 /// Where this sequence's own keys would begin: the length of `keys`
529 /// at its start.
530 mark: usize,
531 at: Place<'t>,
532 next: usize,
533 rules: Option<Held<'t, SeqRules>>,
534 by: N,
535 _error: PhantomData<E>,
536}
537
538impl<E: Error, N: Normalize> SeqSink for TermsSeq<'_, E, N> {
539 type Ok = ();
540 type Error = E;
541 type Element<'s>
542 = Terms<'s, E, N>
543 where
544 Self: 's;
545
546 fn element(&mut self) -> Result<Terms<'_, E, N>, E> {
547 let TermsSeq {
548 out,
549 keys,
550 mark,
551 at,
552 next,
553 rules,
554 by,
555 ..
556 } = self;
557 let keys = keys.get();
558 let rules = match rules {
559 Some(held) => Some(NodeRules {
560 depth: held.node.depth + 1,
561 settle: Some(
562 held.rules
563 .element(&At { place: at, keys })
564 .map_err(refused)?,
565 ),
566 }),
567 None => None,
568 };
569 keys.truncate(*mark);
570 let index = *next;
571 *next += 1;
572 Ok(Terms {
573 out,
574 keys: Keys::Lent(keys),
575 at: Place::Element { parent: at, index },
576 rules,
577 by: *by,
578 _error: PhantomData,
579 })
580 }
581
582 fn end(self) -> Result<(), E> {
583 if let Some(held) = self.rules {
584 let at = At {
585 place: &self.at,
586 keys: self.keys.as_str(),
587 };
588 held.rules.end(&at).map_err(refused)?;
589 held.node.sealed();
590 }
591 Ok(())
592 }
593}
594
595/// The map side of [`Terms`]: each entry's path is the map's plus its
596/// key.
597pub struct TermsMap<'t, E, N: Normalize = Equality> {
598 out: &'t mut Vec<Term<N::Term>>,
599 keys: Keys<'t>,
600 /// Where this map's keys begin in `keys`: its length at the map's
601 /// start. Each entry's key replaces the last one there.
602 mark: usize,
603 at: Place<'t>,
604 rules: Option<Held<'t, MapRules>>,
605 by: N,
606 _error: PhantomData<E>,
607}
608
609impl<E: Error, N: Normalize> MapSink for TermsMap<'_, E, N> {
610 type Ok = ();
611 type Error = E;
612 type Entry<'s>
613 = Terms<'s, E, N>
614 where
615 Self: 's;
616
617 fn entry(&mut self, key: &str) -> Result<Terms<'_, E, N>, E> {
618 let TermsMap {
619 out,
620 keys,
621 mark,
622 at,
623 rules,
624 by,
625 ..
626 } = self;
627 let keys = keys.get();
628 let rules = match rules {
629 Some(held) => Some(NodeRules {
630 depth: held.node.depth + 1,
631 settle: Some(
632 held.rules
633 .entry(key, &At { place: at, keys })
634 .map_err(refused)?,
635 ),
636 }),
637 None => None,
638 };
639 keys.truncate(*mark);
640 keys.push_str(key);
641 let key = (*mark, keys.len());
642 Ok(Terms {
643 out,
644 keys: Keys::Lent(keys),
645 at: Place::Entry { parent: at, key },
646 rules,
647 by: *by,
648 _error: PhantomData,
649 })
650 }
651
652 fn end(self) -> Result<(), E> {
653 if let Some(held) = self.rules {
654 let at = At {
655 place: &self.at,
656 keys: self.keys.as_str(),
657 };
658 held.rules.end(&at).map_err(refused)?;
659 held.node.sealed();
660 }
661 Ok(())
662 }
663}
664
665/// Every equality term of `value`, in walk order, without encrypting it.
666///
667/// It refuses every value encryption refuses, for the same reason at the
668/// same path (see [`Terms`]), so it never returns terms for a value that
669/// cannot be stored.
670///
671/// # Examples
672///
673/// ```
674/// use std::collections::BTreeMap;
675/// use vitaminc_normalize::{domain, terms, NormalizeError, Step};
676///
677/// let mut user = BTreeMap::new();
678/// user.insert("name", "Ada");
679/// let found = terms(&user)?;
680/// assert_eq!(found[0].path.steps(), [Step::Key("name".into())]);
681/// assert_eq!(found[0].normalized.domain, domain::TEXT);
682///
683/// // U+0378 is unassigned in Unicode 16, so the name has no term.
684/// user.insert("name", "\u{378}");
685/// let refused = terms(&user).unwrap_err();
686/// let term = refused.term().expect("refused by Terms");
687/// assert_eq!(term.error(), NormalizeError::UnassignedCodePoint);
688/// assert_eq!(term.path().steps(), [Step::Key("name".into())]);
689/// // The message names neither the field nor its value.
690/// assert_eq!(
691/// refused.to_string(),
692/// "no equality term: text contains a code point unassigned in Unicode 16"
693/// );
694///
695/// // A uuid of three bytes cannot be encrypted, so it has no term either:
696/// // `terms` refuses it as encryption does.
697/// use vitaminc_aead::{Logical, Physical, Raw, Unencryptable};
698/// let id = Raw(Some(Logical::Uuid), Physical::Bytes(&[0; 3]));
699/// let refused = terms(id).unwrap_err();
700/// let refusal = refused.refusal().expect("refused for its shape");
701/// assert_eq!(refusal.reason(), Unencryptable::InvalidValue);
702/// # Ok::<(), vitaminc_normalize::TermsError>(())
703/// ```
704///
705/// # Errors
706///
707/// [`TermsError`]: what [`Terms`] refuses, a value encryption would refuse
708/// included, or the source's own failure.
709pub fn terms<P: Encrypt>(value: P) -> Result<Vec<Term>, TermsError> {
710 terms_by(Equality, value)
711}
712
713/// Every term of `value` under the normalizer `by`, in walk order, without
714/// encrypting it: [`terms`] for an index of another kind.
715///
716/// It refuses what [`terms`] refuses for the value's shape, and what `by`
717/// refuses for a scalar, the way [`Terms`] describes.
718///
719/// # Errors
720///
721/// [`TermsError`]: what [`Terms`] refuses, a value encryption would refuse
722/// included, or the source's own failure.
723pub fn terms_by<N, P>(by: N, value: P) -> Result<Vec<Term<N::Term>>, TermsError>
724where
725 N: Normalize,
726 P: Encrypt,
727{
728 let mut out = Vec::new();
729 value.visit(Terms::by(by, &mut out))?;
730 Ok(out)
731}
732
733/// A ciphertext tree and the terms of the value it was sealed from, in
734/// walk order.
735pub type WithTerms<L> = (Node<L>, Vec<Term>);
736
737/// Walk `value` into the encrypting sink and the terms sink at once,
738/// without sealing: the planned tree of pending leaves, and every term.
739///
740/// # Errors
741///
742/// Everything [`EncryptSink`] refuses, plus what [`Terms`] refuses, which
743/// arrives as [`EncryptError::Source`] carrying a [`TermError`].
744pub fn plan_with_terms<'a, P, E, A>(value: P, aad: A) -> Result<WithTerms<Pending>, EncryptError<E>>
745where
746 P: Encrypt,
747 A: IntoAad<'a>,
748{
749 let mut terms = Vec::new();
750 let (planned, ()) = value.visit(Tee(
751 EncryptSink::root(aad),
752 Terms::beside_encryption(&mut terms),
753 ))?;
754 Ok((planned, terms))
755}
756
757/// Encrypt `value` and produce its equality terms in the same walk.
758///
759/// # Errors
760///
761/// As [`plan_with_terms`], then [`EncryptError::Cipher`] if the cipher
762/// refuses a leaf.
763pub fn encrypt_with_terms<'a, P, C, A>(
764 value: P,
765 cipher: &C,
766 aad: A,
767) -> Result<WithTerms<C::Leaf>, EncryptError<C::Error>>
768where
769 P: Encrypt,
770 C: Cipher,
771 A: IntoAad<'a>,
772{
773 let (planned, terms) = plan_with_terms(value, aad)?;
774 Ok((seal_all(cipher, planned)?, terms))
775}
776
777/// Encrypt `value` through an async cipher and produce its equality terms
778/// in the same walk. The walk, and so every term, is complete before the
779/// future is returned, so the future holds only the planned leaves and is
780/// `Send` even when the source is not.
781///
782/// # Errors
783///
784/// As [`encrypt_with_terms`]. A refusal during the walk is held in the
785/// future and returned on its first poll.
786pub fn encrypt_with_terms_async<'a, 'c, P, C, A>(
787 value: P,
788 cipher: &'c C,
789 aad: A,
790) -> impl Future<Output = Result<WithTerms<C::Leaf>, EncryptError<C::Error>>> + Send + 'c
791where
792 P: Encrypt,
793 C: AsyncCipher,
794 A: IntoAad<'a>,
795{
796 let planned = plan_with_terms(value, aad);
797 async move {
798 let (planned, terms) = planned?;
799 Ok((seal_all_async(cipher, planned).await?, terms))
800 }
801}