vitaminc_ore/target.rs
1//! Targets a caller names when the program is compiled, and the tables
2//! that say what fits them.
3//!
4//! An order space declares the type every value in it is encoded as: its
5//! target. [`OrderTarget`] names one while the program runs, as an index
6//! layer that reads its column types from a schema must. The zero-sized
7//! markers here name one when the program is compiled, so the compiler can
8//! check what goes in: [`FitsIn`] lists the conversions that never lose
9//! information, and [`OwnTarget`] gives a Rust type the target it is.
10//! Both are sealed tables, generated in this module, so no caller can add
11//! a conversion that might be refused or infer a target from a value.
12//!
13//! The same method takes either: [`OrderEncrypt::target`] is generic over
14//! [`Target`], which every marker and [`OrderTarget`] implement.
15//!
16//! A space can also place nulls first or last, and the policy follows the
17//! target. On a fixed-width marker it is a marker too, [`First`] or
18//! [`Last`], and the space's target becomes [`Nulls<T, P>`](Nulls): the
19//! compiler keeps a nulls-first term out of a nulls-last space's
20//! comparison, as it keeps an `I64` term out of an `I32` space's. On an
21//! [`OrderTarget`] it is a [`NullOrder`] chosen while the program runs,
22//! and the target becomes a [`RuntimeNulls`], whose terms carry their
23//! policy and compare as `None` with a term of the other.
24//!
25//! [`OrderEncrypt::target`]: crate::OrderEncrypt::target
26
27use core::fmt;
28use core::marker::PhantomData;
29
30use vitaminc_aead::{Bytes as RawBytes, Encrypt};
31
32use crate::{NullOrder, OrderScheme, OrderTarget};
33
34mod private {
35 /// Seals [`Target`](super::Target), [`FitsIn`](super::FitsIn) and
36 /// [`OwnTarget`](super::OwnTarget) to this crate.
37 pub trait Sealed {}
38}
39
40/// A target an order space can be opened with: one of the zero-sized
41/// markers in this module, named when the program is compiled, or an
42/// [`OrderTarget`], chosen while it runs.
43///
44/// Sealed: every target is one this crate encodes.
45pub trait Target: private::Sealed + Copy + fmt::Debug {
46 /// The target as the value every encoding is keyed by.
47 fn runtime(self) -> OrderTarget;
48
49 /// Where a space of this target places nulls: `None`, unless the
50 /// target is a [`Nulls`] or a [`RuntimeNulls`], which a space's
51 /// `nulls` method makes.
52 fn null_order(self) -> Option<NullOrder> {
53 None
54 }
55}
56
57/// A target known when the program is compiled: one of the markers in this
58/// module. What a space of such a target takes is checked by the compiler,
59/// through [`FitsIn`].
60pub trait StaticTarget: Target + Default {
61 /// The target this marker stands for.
62 const TARGET: OrderTarget;
63}
64
65/// A marker target of fixed width: every marker but [`Text`] and
66/// [`Bytes`]. Only these take a null policy, so
67/// `scheme.target(I32).nulls(Last)` compiles and
68/// `scheme.target(Text).nulls(Last)` does not.
69///
70/// Sealed: implemented for the thirteen fixed-width markers here.
71#[diagnostic::on_unimplemented(
72 message = "`{Self}` takes no null policy",
73 label = "not a fixed-width target",
74 note = "only a fixed-width target places nulls first or last: a text or bytes null already shows by its length, so placing it would hide nothing"
75)]
76pub trait FixedWidth: StaticTarget {}
77
78/// The null policy of a space whose target is named when the program is
79/// compiled: [`First`] or [`Last`], which a marker space's `nulls` method
80/// takes. The policy is then part of the space's type, as the target is.
81///
82/// Sealed: [`First`] and [`Last`] are the only policies.
83pub trait NullPolicy: private::Sealed + Copy + fmt::Debug + Default {
84 /// Where a space of this policy places nulls.
85 const ORDER: NullOrder;
86}
87
88/// Nulls sort below every value, as SQL's `NULLS FIRST`: the policy
89/// [`NullOrder::First`], named when the program is compiled.
90#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
91pub struct First;
92
93/// Nulls sort above every value, as SQL's `NULLS LAST`: the policy
94/// [`NullOrder::Last`], named when the program is compiled.
95#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
96pub struct Last;
97
98impl private::Sealed for First {}
99impl private::Sealed for Last {}
100
101impl NullPolicy for First {
102 const ORDER: NullOrder = NullOrder::First;
103}
104
105impl NullPolicy for Last {
106 const ORDER: NullOrder = NullOrder::Last;
107}
108
109/// A marker target in a space that places nulls first or last: the
110/// target `T` and the policy `P`, both part of the type. Made only by a
111/// marker space's `nulls` method, from a fixed-width target, so every
112/// `Nulls` names a domain label.
113///
114/// A space of a `Nulls` target is not a [`StaticTarget`] space, so it has
115/// no `encrypt`: the policy refuses the maximum of an integer or uuid
116/// target, so a value goes through `encrypt_checked`, which reports it.
117/// Terms of a `Nulls<I32, Last>` space do not compile against an `I32`
118/// space's comparison, nor against a `Nulls<I32, First>` space's.
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120pub struct Nulls<T, P: NullPolicy> {
121 target: T,
122 policy: PhantomData<P>,
123}
124
125impl<T: FixedWidth, P: NullPolicy> Nulls<T, P> {
126 pub(crate) fn new(target: T) -> Self {
127 Self {
128 target,
129 policy: PhantomData,
130 }
131 }
132
133 /// The target the policy is set on.
134 pub fn target(self) -> T {
135 self.target
136 }
137
138 /// Where the space places nulls: `P`'s order.
139 pub fn null_order(self) -> NullOrder {
140 P::ORDER
141 }
142}
143
144impl<T: FixedWidth, P: NullPolicy> private::Sealed for Nulls<T, P> {}
145
146impl<T: FixedWidth, P: NullPolicy> Target for Nulls<T, P> {
147 fn runtime(self) -> OrderTarget {
148 self.target.runtime()
149 }
150
151 fn null_order(self) -> Option<NullOrder> {
152 Some(P::ORDER)
153 }
154}
155
156/// A scheme takes a target in a space that places nulls exactly when it
157/// takes the target: the policy changes no width.
158impl<S: Supports<T> + ?Sized, T: FixedWidth, P: NullPolicy> Supports<Nulls<T, P>> for S {
159 fn supports(target: Nulls<T, P>) -> bool {
160 <S as Supports<T>>::supports(target.target)
161 }
162}
163
164/// A target chosen while the program runs, in a space that places nulls
165/// first or last: the [`OrderTarget`] and its [`NullOrder`], both values.
166/// Made only by `nulls` on a space of an [`OrderTarget`], which refuses
167/// text and bytes, so every `RuntimeNulls` names a domain label.
168///
169/// This is for a layer that reads its column types and null policies from
170/// a schema while the program runs. Every term of such a space carries its
171/// policy, as it carries its target, so a nulls-first and a nulls-last
172/// term of one target compare as `None` rather than as noise.
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
174pub struct RuntimeNulls {
175 target: OrderTarget,
176 order: NullOrder,
177}
178
179impl RuntimeNulls {
180 pub(crate) fn new(target: OrderTarget, order: NullOrder) -> Self {
181 Self { target, order }
182 }
183
184 /// The target the policy is set on.
185 pub fn target(self) -> OrderTarget {
186 self.target
187 }
188
189 /// Where the space places nulls.
190 pub fn null_order(self) -> NullOrder {
191 self.order
192 }
193}
194
195impl private::Sealed for RuntimeNulls {}
196
197impl Target for RuntimeNulls {
198 fn runtime(self) -> OrderTarget {
199 self.target
200 }
201
202 fn null_order(self) -> Option<NullOrder> {
203 Some(self.order)
204 }
205}
206
207/// A scheme takes a target chosen while the program runs, in a space that
208/// places nulls, exactly when it takes the target.
209impl<S: Supports<OrderTarget> + ?Sized> Supports<RuntimeNulls> for S {
210 fn supports(target: RuntimeNulls) -> bool {
211 <S as Supports<OrderTarget>>::supports(target.target)
212 }
213}
214
215impl private::Sealed for OrderTarget {}
216
217impl Target for OrderTarget {
218 fn runtime(self) -> OrderTarget {
219 self
220 }
221}
222
223/// A value that always fits target `T`: the conversion loses nothing and
224/// the encoding refuses nothing, so [`OrderSpace::encrypt`] can take it
225/// without a value-refusal arm in its error.
226///
227/// A sealed table, one impl per conversion:
228///
229/// | Target | Takes |
230/// | --- | --- |
231/// | an integer target | every integer type whose whole range it holds, so `I32` takes `i8`, `i16`, `i32` and `u16` (a `u8` is not a scalar of the data model: `Vec<u8>` is bytes). There is no 8-bit target, so an `i8` fits `I16` and up: see [`OrderTarget`] |
232/// | `F64` | `f32`, `f64` |
233/// | `Bytes` | `&[u8]`, `Vec<u8>`, `[u8; N]`, [`Bytes`](vitaminc_aead::Bytes) |
234/// | `Date` | `chrono::NaiveDate` (feature `chrono`) |
235/// | `Timestamp` | `vitaminc_aead::Timestamp` (feature `chrono`) |
236/// | `Decimal` | `rust_decimal::Decimal` (feature `rust_decimal`) |
237/// | `Uuid` | `uuid::Uuid` (feature `uuid`) |
238///
239/// and a reference to any of them. What is not here can be refused: an
240/// `i64` may not fit `I32`, text may hold a code point Unicode 16 does not
241/// assign, and a `chrono::DateTime<Utc>` may be a leap second. Those go
242/// through [`OrderSpace::encrypt_checked`], which checks the value while
243/// the program runs.
244///
245/// [`OrderSpace::encrypt`]: crate::OrderSpace::encrypt
246/// [`OrderSpace::encrypt_checked`]: crate::OrderSpace::encrypt_checked
247#[diagnostic::on_unimplemented(
248 message = "`{Self}` is not a value that always fits `{T}`",
249 label = "does not always fit `{T}`",
250 note = "`encrypt` takes a value the encoding never refuses: a narrower integer, an `f32`, bytes, or a typed value such as a `NaiveDate`",
251 note = "a value the encoding can refuse (an integer that may be out of range, text, a `DateTime<Utc>`, or any other `Encrypt` value) goes through `encrypt_checked`, which checks it while the program runs"
252)]
253pub trait FitsIn<T: StaticTarget>: Encrypt + private::Sealed {}
254
255/// A Rust type's own target, for [`OrderEncrypt::encrypt`] and the other
256/// chains that name none: an `i64` is an `I64`, an `f32` an `F64`, a
257/// `&str` a `Text`.
258///
259/// Sealed, and implemented for the data model's own types only. A target
260/// is never inferred from a value: a JavaScript `BigInt` arrives as the
261/// narrowest integer that holds it, and inferring from the value would
262/// scatter one index across several domains. A newtype names its target
263/// with [`OrderEncrypt::target`]. An `i8` has no own target: an order
264/// term over 256 values reveals them (see [`OrderTarget`]), so an `i8`
265/// goes into a wider target it fits, by name:
266/// `scheme.target(I16).encrypt(value)`.
267///
268/// [`OrderEncrypt::encrypt`]: crate::OrderEncrypt::encrypt
269/// [`OrderEncrypt::target`]: crate::OrderEncrypt::target
270#[diagnostic::on_unimplemented(
271 message = "`{Self}` has no order target of its own",
272 label = "has no order target of its own",
273 note = "name the target: `scheme.target(I64).encrypt_checked(value)`",
274 note = "there is no 8-bit target: an order term over 256 values reveals them, so an `i8` goes into `I16` or wider by name (`scheme.target(I16).encrypt(value)`), or into an equality index"
275)]
276pub trait OwnTarget: Encrypt + private::Sealed {
277 /// The target this type is.
278 type Target: StaticTarget;
279}
280
281/// What a scheme takes: implemented by a scheme crate for every target
282/// its scheme encrypts, so that `scheme.target(Text)` on a fixed-width
283/// scheme fails to compile rather than returning
284/// [`OrderError::Unsupported`](crate::OrderError::Unsupported).
285///
286/// A scheme answers twice. `Supports<OrderTarget>` is required of every
287/// scheme (no space of any target opens without it), and its
288/// [`supports`](Self::supports) says which targets the scheme takes when
289/// one is chosen while the program runs. `Supports<I32>` and the other
290/// marker impls say the same when the target is named as the program is
291/// compiled. A scheme writes both at once with
292/// [`supports_targets!`](crate::supports_targets), from one list, so
293/// they cannot disagree; a scheme that takes every target writes
294/// [`supports_every_target!`](crate::supports_every_target). A space
295/// asks the runtime answer for every target it is opened with, and in a
296/// debug build asserts that a marker's answer agrees.
297///
298/// A scheme never answers for a target in a space that places nulls
299/// ([`Nulls`], [`RuntimeNulls`]): this crate answers for it, with the
300/// answer for the target
301/// the policy is set on, since the policy changes no width. Those impls
302/// cover every scheme, so a scheme cannot implement `Supports` for every
303/// `T: Target` at once; it lists its targets, as the macros do.
304#[diagnostic::on_unimplemented(
305 message = "`{Self}` has no order term for `{T}`",
306 label = "does not implement `Supports<{T}>`",
307 note = "a scheme lists the targets it takes with `vitaminc_ore::supports_targets!(Scheme; I32, Date)`, which answers for each marker and for `OrderTarget`; pick one it lists, or an `OrderTarget` chosen while the program runs"
308)]
309pub trait Supports<T: Target>: OrderScheme {
310 /// Whether the scheme takes terms of `target`. For a marker, `true`
311 /// (the impl exists); for an [`OrderTarget`], the scheme's list.
312 fn supports(target: T) -> bool;
313}
314
315/// Implement [`Supports`] for the targets a scheme takes, from one list:
316/// once for [`OrderTarget`], answering `true` for a listed target, and
317/// once per listed marker.
318///
319/// ```
320/// # use vitaminc_ore::{Context, OrderPlaintext, OrderScheme};
321/// # struct TenBytes;
322/// # impl OrderScheme for TenBytes {
323/// # const NAME: &'static str = "ten-bytes";
324/// # type Term = Vec<u8>;
325/// # type Error = core::convert::Infallible;
326/// # type Space = ();
327/// # fn open(&self, _: vitaminc_ore::OrderTarget, _: &Context<'_>) -> Result<(), Self::Error> {
328/// # Ok(())
329/// # }
330/// # fn encrypt_plaintext(&self, _: &(), p: &OrderPlaintext) -> Result<Vec<u8>, Self::Error> {
331/// # use vitaminc_protected::Controlled;
332/// # Ok(p.bytes().risky_ref().clone())
333/// # }
334/// # fn compare(a: &Vec<u8>, b: &Vec<u8>) -> Option<core::cmp::Ordering> { Some(a.cmp(b)) }
335/// # }
336/// // A fixed-width scheme over at most ten bytes.
337/// vitaminc_ore::supports_targets!(TenBytes; I16, U16, I32, U32, I64, U64, F64, Date);
338/// ```
339#[macro_export]
340macro_rules! supports_targets {
341 ($scheme:ty; $($marker:ident),* $(,)?) => {
342 impl $crate::Supports<$crate::OrderTarget> for $scheme {
343 fn supports(target: $crate::OrderTarget) -> bool {
344 #[allow(unreachable_patterns)]
345 match target {
346 $($crate::OrderTarget::$marker => true,)*
347 _ => false,
348 }
349 }
350 }
351 $(
352 impl $crate::Supports<$crate::target::$marker> for $scheme {
353 fn supports(_: $crate::target::$marker) -> bool {
354 true
355 }
356 }
357 )*
358 };
359}
360
361/// Implement [`Supports`] for every target, for a scheme that takes them
362/// all, as the CLWW schemes do: [`supports_targets!`] with the full list.
363///
364/// ```
365/// # use vitaminc_ore::{Context, OrderPlaintext, OrderScheme};
366/// # struct Cllw;
367/// # impl OrderScheme for Cllw {
368/// # const NAME: &'static str = "cllw";
369/// # type Term = Vec<u8>;
370/// # type Error = core::convert::Infallible;
371/// # type Space = ();
372/// # fn open(&self, _: vitaminc_ore::OrderTarget, _: &Context<'_>) -> Result<(), Self::Error> {
373/// # Ok(())
374/// # }
375/// # fn encrypt_plaintext(&self, _: &(), p: &OrderPlaintext) -> Result<Vec<u8>, Self::Error> {
376/// # use vitaminc_protected::Controlled;
377/// # Ok(p.bytes().risky_ref().clone())
378/// # }
379/// # fn compare(a: &Vec<u8>, b: &Vec<u8>) -> Option<core::cmp::Ordering> { Some(a.cmp(b)) }
380/// # }
381/// vitaminc_ore::supports_every_target!(Cllw);
382/// ```
383#[macro_export]
384macro_rules! supports_every_target {
385 ($scheme:ty) => {
386 $crate::supports_targets!($scheme;
387 I16, U16, I32, U32, I64, U64, I128, U128,
388 F64, Date, Timestamp, Decimal, Text, Bytes, Uuid);
389 };
390}
391
392/// One marker per target, each a zero-sized `Copy` type that stands for
393/// its [`OrderTarget`] variant.
394macro_rules! markers {
395 ($($(#[$doc:meta])* $marker:ident),* $(,)?) => {
396 $(
397 $(#[$doc])*
398 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
399 pub struct $marker;
400
401 impl private::Sealed for $marker {}
402
403 impl Target for $marker {
404 fn runtime(self) -> OrderTarget {
405 OrderTarget::$marker
406 }
407 }
408
409 impl StaticTarget for $marker {
410 const TARGET: OrderTarget = OrderTarget::$marker;
411 }
412 )*
413 };
414}
415
416/// The fixed-width markers: every one but `Text` and `Bytes`.
417macro_rules! fixed_width {
418 ($($marker:ident),* $(,)?) => {
419 $(impl FixedWidth for $marker {})*
420 };
421}
422
423fixed_width!(I16, U16, I32, U32, I64, U64, I128, U128, F64, Date, Timestamp, Decimal, Uuid);
424
425markers! {
426 /// [`OrderTarget::I16`]. The narrowest integer target: an 8-bit one
427 /// would reveal its values, as [`OrderTarget`] explains.
428 I16,
429 /// [`OrderTarget::U16`].
430 U16,
431 /// [`OrderTarget::I32`].
432 I32,
433 /// [`OrderTarget::U32`].
434 U32,
435 /// [`OrderTarget::I64`].
436 I64,
437 /// [`OrderTarget::U64`].
438 U64,
439 /// [`OrderTarget::I128`].
440 I128,
441 /// [`OrderTarget::U128`].
442 U128,
443 /// [`OrderTarget::F64`].
444 F64,
445 /// [`OrderTarget::Date`].
446 Date,
447 /// [`OrderTarget::Timestamp`].
448 Timestamp,
449 /// [`OrderTarget::Decimal`].
450 Decimal,
451 /// [`OrderTarget::Text`].
452 Text,
453 /// [`OrderTarget::Bytes`].
454 Bytes,
455 /// [`OrderTarget::Uuid`].
456 Uuid,
457}
458
459/// Seal the value types the tables below name, by value and by reference.
460macro_rules! sealed {
461 ($($ty:ty),* $(,)?) => {
462 $(
463 impl private::Sealed for $ty {}
464 impl private::Sealed for &$ty {}
465 )*
466 };
467}
468
469/// `FitsIn` rows: each target, then every type that always fits it, by
470/// value and by reference.
471macro_rules! fits {
472 ($($target:ident: $($ty:ty),*;)*) => {
473 $($(
474 impl FitsIn<$target> for $ty {}
475 impl FitsIn<$target> for &$ty {}
476 )*)*
477 };
478}
479
480/// `OwnTarget` rows: a type and the target it is, by value and by
481/// reference.
482macro_rules! own {
483 ($($ty:ty => $target:ident),* $(,)?) => {
484 $(
485 impl OwnTarget for $ty {
486 type Target = $target;
487 }
488 impl OwnTarget for &$ty {
489 type Target = $target;
490 }
491 )*
492 };
493}
494
495sealed!(
496 i8,
497 i16,
498 u16,
499 i32,
500 u32,
501 i64,
502 u64,
503 i128,
504 u128,
505 f32,
506 f64,
507 str,
508 String,
509 [u8],
510 Vec<u8>,
511);
512
513impl private::Sealed for RawBytes<'_> {}
514impl private::Sealed for &RawBytes<'_> {}
515impl<const N: usize> private::Sealed for [u8; N] {}
516impl<const N: usize> private::Sealed for &[u8; N] {}
517
518// An integer target takes every integer type whose whole range it holds:
519// a signed target holds a narrower type of either sign, an unsigned
520// target only a narrower unsigned one. There is no `u8` row: a `u8` is
521// not a scalar of the data model (a `Vec<u8>` is bytes), so it has no
522// `Encrypt` impl to fit with.
523fits! {
524 I16: i8, i16;
525 U16: u16;
526 I32: i8, i16, i32, u16;
527 U32: u16, u32;
528 I64: i8, i16, i32, i64, u16, u32;
529 U64: u16, u32, u64;
530 I128: i8, i16, i32, i64, i128, u16, u32, u64;
531 U128: u16, u32, u64, u128;
532 F64: f32, f64;
533 Bytes: Vec<u8>;
534}
535
536impl FitsIn<Bytes> for &[u8] {}
537impl FitsIn<Bytes> for RawBytes<'_> {}
538impl FitsIn<Bytes> for &RawBytes<'_> {}
539impl<const N: usize> FitsIn<Bytes> for [u8; N] {}
540impl<const N: usize> FitsIn<Bytes> for &[u8; N] {}
541
542own! {
543 i16 => I16,
544 u16 => U16,
545 i32 => I32,
546 u32 => U32,
547 i64 => I64,
548 u64 => U64,
549 i128 => I128,
550 u128 => U128,
551 f32 => F64,
552 f64 => F64,
553 String => Text,
554 Vec<u8> => Bytes,
555}
556
557impl OwnTarget for &str {
558 type Target = Text;
559}
560impl OwnTarget for &[u8] {
561 type Target = Bytes;
562}
563impl OwnTarget for RawBytes<'_> {
564 type Target = Bytes;
565}
566impl OwnTarget for &RawBytes<'_> {
567 type Target = Bytes;
568}
569impl<const N: usize> OwnTarget for [u8; N] {
570 type Target = Bytes;
571}
572impl<const N: usize> OwnTarget for &[u8; N] {
573 type Target = Bytes;
574}
575
576// The typed values callers hold. Each is inside the range its encoding
577// takes for every value the type can hold: chrono's `NaiveDate` spans
578// exactly the day range `Logical::Date` admits, a `Timestamp` is never a
579// leap second and lies in chrono's range, and a `rust_decimal::Decimal`
580// has a 96-bit mantissa and a scale of at most 28, which is what the
581// decimal encoding takes. A `chrono::DateTime<Utc>` may be a leap second,
582// which has no count, so it is checked, not fitting.
583#[cfg(feature = "chrono")]
584mod chrono_rows {
585 use super::*;
586 use chrono::{DateTime, NaiveDate, Utc};
587 use vitaminc_aead::Timestamp as Instant;
588
589 impl private::Sealed for NaiveDate {}
590 impl private::Sealed for &NaiveDate {}
591 impl private::Sealed for DateTime<Utc> {}
592 impl private::Sealed for &DateTime<Utc> {}
593 impl private::Sealed for Instant {}
594 impl private::Sealed for &Instant {}
595
596 impl FitsIn<Date> for NaiveDate {}
597 impl FitsIn<Date> for &NaiveDate {}
598 impl FitsIn<Timestamp> for Instant {}
599 impl FitsIn<Timestamp> for &Instant {}
600
601 impl OwnTarget for NaiveDate {
602 type Target = Date;
603 }
604 impl OwnTarget for &NaiveDate {
605 type Target = Date;
606 }
607 impl OwnTarget for DateTime<Utc> {
608 type Target = Timestamp;
609 }
610 impl OwnTarget for &DateTime<Utc> {
611 type Target = Timestamp;
612 }
613 impl OwnTarget for Instant {
614 type Target = Timestamp;
615 }
616 impl OwnTarget for &Instant {
617 type Target = Timestamp;
618 }
619}
620
621#[cfg(feature = "rust_decimal")]
622mod decimal_rows {
623 use super::*;
624
625 impl private::Sealed for rust_decimal::Decimal {}
626 impl private::Sealed for &rust_decimal::Decimal {}
627 impl FitsIn<Decimal> for rust_decimal::Decimal {}
628 impl FitsIn<Decimal> for &rust_decimal::Decimal {}
629 impl OwnTarget for rust_decimal::Decimal {
630 type Target = Decimal;
631 }
632 impl OwnTarget for &rust_decimal::Decimal {
633 type Target = Decimal;
634 }
635}
636
637#[cfg(feature = "uuid")]
638mod uuid_rows {
639 use super::*;
640
641 impl private::Sealed for uuid::Uuid {}
642 impl private::Sealed for &uuid::Uuid {}
643 impl FitsIn<Uuid> for uuid::Uuid {}
644 impl FitsIn<Uuid> for &uuid::Uuid {}
645 impl OwnTarget for uuid::Uuid {
646 type Target = Uuid;
647 }
648 impl OwnTarget for &uuid::Uuid {
649 type Target = Uuid;
650 }
651}
652
653#[cfg(test)]
654mod tests {
655 use super::*;
656
657 #[test]
658 fn every_marker_stands_for_its_target_and_every_target_is_itself() {
659 fn check<T: StaticTarget>(expected: OrderTarget) {
660 assert_eq!(T::TARGET, expected);
661 assert_eq!(T::default().runtime(), expected);
662 }
663 check::<I16>(OrderTarget::I16);
664 check::<U16>(OrderTarget::U16);
665 check::<I32>(OrderTarget::I32);
666 check::<U32>(OrderTarget::U32);
667 check::<I64>(OrderTarget::I64);
668 check::<U64>(OrderTarget::U64);
669 check::<I128>(OrderTarget::I128);
670 check::<U128>(OrderTarget::U128);
671 check::<F64>(OrderTarget::F64);
672 check::<Date>(OrderTarget::Date);
673 check::<Timestamp>(OrderTarget::Timestamp);
674 check::<Decimal>(OrderTarget::Decimal);
675 check::<Text>(OrderTarget::Text);
676 check::<Bytes>(OrderTarget::Bytes);
677 check::<Uuid>(OrderTarget::Uuid);
678 for target in OrderTarget::ALL {
679 assert_eq!(target.runtime(), *target);
680 }
681 }
682
683 /// A four-byte scheme, the shape of block ORE at one width.
684 struct Four;
685
686 impl OrderScheme for Four {
687 const NAME: &'static str = "four";
688 type Term = Vec<u8>;
689 type Error = core::convert::Infallible;
690 type Space = ();
691
692 fn open(
693 &self,
694 _: OrderTarget,
695 _: &vitaminc_context::Context<'_>,
696 ) -> Result<(), Self::Error> {
697 Ok(())
698 }
699
700 fn encrypt_plaintext(
701 &self,
702 _: &(),
703 _: &crate::OrderPlaintext,
704 ) -> Result<Vec<u8>, Self::Error> {
705 Ok(vec![0])
706 }
707
708 fn compare(a: &Vec<u8>, b: &Vec<u8>) -> Option<core::cmp::Ordering> {
709 Some(a.cmp(b))
710 }
711 }
712
713 crate::supports_targets!(Four; I32, U32, Date);
714
715 #[test]
716 fn a_scheme_takes_a_target_with_a_null_policy_exactly_when_it_takes_the_target() {
717 fn marker<P: NullPolicy>(order: NullOrder) {
718 assert!(
719 <Four as Supports<Nulls<I32, P>>>::supports(Nulls::new(I32)),
720 "{order:?}: a marker the scheme takes"
721 );
722 let placed = Nulls::<Date, P>::new(Date);
723 assert_eq!(placed.target(), Date);
724 assert_eq!(placed.null_order(), order);
725 assert_eq!(placed.runtime(), OrderTarget::Date);
726 assert_eq!(Target::null_order(placed), Some(order));
727 }
728 marker::<First>(NullOrder::First);
729 marker::<Last>(NullOrder::Last);
730 for order in [NullOrder::First, NullOrder::Last] {
731 assert!(
732 <Four as Supports<RuntimeNulls>>::supports(RuntimeNulls::new(
733 OrderTarget::Date,
734 order
735 )),
736 "{order:?}: a runtime target the scheme takes"
737 );
738 assert!(
739 !<Four as Supports<RuntimeNulls>>::supports(RuntimeNulls::new(
740 OrderTarget::I64,
741 order
742 )),
743 "{order:?}: a runtime target the scheme does not take"
744 );
745 let placed = RuntimeNulls::new(OrderTarget::Date, order);
746 assert_eq!(placed.target(), OrderTarget::Date);
747 assert_eq!(placed.null_order(), order);
748 assert_eq!(placed.runtime(), OrderTarget::Date);
749 assert_eq!(Target::null_order(placed), Some(order));
750 }
751 assert_eq!(I32.null_order(), None, "a marker places no nulls");
752 assert_eq!(
753 OrderTarget::I32.null_order(),
754 None,
755 "nor does a runtime target"
756 );
757 }
758
759 /// Four bytes again, with a hand-written marker answer that refuses
760 /// `U32`, so the policy impls can be seen to ask the target's answer
761 /// rather than assume one.
762 struct Refusing;
763
764 impl OrderScheme for Refusing {
765 const NAME: &'static str = "refusing";
766 type Term = Vec<u8>;
767 type Error = core::convert::Infallible;
768 type Space = ();
769
770 fn open(
771 &self,
772 _: OrderTarget,
773 _: &vitaminc_context::Context<'_>,
774 ) -> Result<(), Self::Error> {
775 Ok(())
776 }
777
778 fn encrypt_plaintext(
779 &self,
780 _: &(),
781 _: &crate::OrderPlaintext,
782 ) -> Result<Vec<u8>, Self::Error> {
783 Ok(vec![0])
784 }
785
786 fn compare(a: &Vec<u8>, b: &Vec<u8>) -> Option<core::cmp::Ordering> {
787 Some(a.cmp(b))
788 }
789 }
790
791 impl Supports<OrderTarget> for Refusing {
792 fn supports(_: OrderTarget) -> bool {
793 false
794 }
795 }
796
797 impl Supports<U32> for Refusing {
798 fn supports(_: U32) -> bool {
799 false
800 }
801 }
802
803 #[test]
804 fn a_policy_target_takes_the_answer_of_the_target_it_is_set_on() {
805 fn marker<P: NullPolicy>() {
806 assert!(
807 !<Refusing as Supports<Nulls<U32, P>>>::supports(Nulls::new(U32)),
808 "{:?}: a marker the scheme refuses stays refused under a policy",
809 P::ORDER
810 );
811 }
812 marker::<First>();
813 marker::<Last>();
814 }
815
816 #[test]
817 fn each_policy_marker_stands_for_its_null_order() {
818 assert_eq!(First::ORDER, NullOrder::First);
819 assert_eq!(Last::ORDER, NullOrder::Last);
820 }
821}