Skip to main content

vitaminc_protected/
non_empty.rs

1//! Non-empty context values. This module is private; its documentation lives
2//! on [`MaybeEmpty`] (what "empty" means) and [`NonEmpty`] (why, and how to
3//! build one).
4
5use std::borrow::Cow;
6
7/// Structural emptiness of a value, decided **before** any encoding is applied.
8///
9/// A type implementing `MaybeEmpty` is one that can be *asked* whether a given
10/// value is empty, not one whose values are empty: integers implement it and
11/// are never empty. It is the bound [`NonEmpty::new`] checks against.
12///
13/// A value is empty when it carries no caller-supplied bytes:
14///
15/// | value | empty when |
16/// |---|---|
17/// | `()` | always |
18/// | `str`, `String`, `[u8]`, `[u8; N]`, `Vec<u8>` | `len() == 0` |
19/// | `Cow<T>` | its referent is empty |
20/// | integers | never — a number is caller information |
21/// | `Option<T>` | `None`, or `Some(t)` with `t` empty |
22/// | `(A, B)` | **both** components empty |
23///
24/// A composite is empty only when it contributes nothing: `("", "email")`
25/// still separates from `("", "id")`, so it is not empty, whereas `("", "")`,
26/// `Some("")` and `Some(None)` are. This is the definition a downstream crate
27/// would otherwise have to reconstruct by parsing the encoded bytes.
28///
29/// [`NonEmpty<T>`] deliberately does **not** implement `MaybeEmpty`, so
30/// [`NonEmpty::new`] checks a composite as a whole
31/// (`NonEmpty::new(("tenant", "email"))`), never a proven part nested inside
32/// a larger value — that would force the outer check to be re-derived anyway.
33/// Extending a proven value is [`NonEmpty::with`]'s job, and it checks
34/// nothing, so its tail needs no `MaybeEmpty` impl at all.
35///
36/// Because the check runs before encoding, *already-encoded* contexts (an
37/// encoded `Context`) do not implement `MaybeEmpty` either: framing makes
38/// their bytes non-empty even when built from an empty value, so an encoded
39/// byte check would certify exactly the degenerate case `NonEmpty` exists to
40/// exclude. Check the value on its way *into* vitaminc, before it is framed.
41///
42/// Implement this for your own context types so they can be wrapped in
43/// [`NonEmpty`].
44pub trait MaybeEmpty {
45    /// Returns `true` if this value carries no caller-supplied bytes.
46    fn is_empty(&self) -> bool;
47}
48
49impl MaybeEmpty for () {
50    fn is_empty(&self) -> bool {
51        true
52    }
53}
54
55impl MaybeEmpty for str {
56    fn is_empty(&self) -> bool {
57        str::is_empty(self)
58    }
59}
60
61impl MaybeEmpty for String {
62    fn is_empty(&self) -> bool {
63        String::is_empty(self)
64    }
65}
66
67impl MaybeEmpty for [u8] {
68    fn is_empty(&self) -> bool {
69        <[u8]>::is_empty(self)
70    }
71}
72
73impl<const N: usize> MaybeEmpty for [u8; N] {
74    fn is_empty(&self) -> bool {
75        N == 0
76    }
77}
78
79impl MaybeEmpty for Vec<u8> {
80    fn is_empty(&self) -> bool {
81        Vec::is_empty(self)
82    }
83}
84
85/// A `Cow` is as empty as its referent, whichever side it holds.
86impl<T> MaybeEmpty for Cow<'_, T>
87where
88    T: MaybeEmpty + ToOwned + ?Sized,
89{
90    fn is_empty(&self) -> bool {
91        T::is_empty(self.as_ref())
92    }
93}
94
95impl<T> MaybeEmpty for &T
96where
97    T: MaybeEmpty + ?Sized,
98{
99    fn is_empty(&self) -> bool {
100        T::is_empty(self)
101    }
102}
103
104macro_rules! never_empty {
105    ($($ty:ty),+ $(,)?) => {$(
106        /// An integer is caller information, so it is never empty.
107        impl MaybeEmpty for $ty {
108            fn is_empty(&self) -> bool {
109                false
110            }
111        }
112
113        /// An integer is never empty, so it converts without a check —
114        /// `NonEmpty::from(7u64)` or `7u64.into()` — where a string would
115        /// need [`NonEmpty::new`] or [`nonempty!`](crate::nonempty).
116        impl From<$ty> for NonEmpty<$ty> {
117            fn from(value: $ty) -> Self {
118                NonEmpty(value)
119            }
120        }
121    )+};
122}
123
124never_empty!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
125
126/// `None` is empty; `Some(value)` is as empty as `value`.
127impl<T> MaybeEmpty for Option<T>
128where
129    T: MaybeEmpty,
130{
131    fn is_empty(&self) -> bool {
132        match self {
133            Some(value) => value.is_empty(),
134            None => true,
135        }
136    }
137}
138
139/// A pair is empty only when **both** components are: a composite that still
140/// contributes caller bytes on either side is not the degenerate case.
141impl<A, B> MaybeEmpty for (A, B)
142where
143    A: MaybeEmpty,
144    B: MaybeEmpty,
145{
146    fn is_empty(&self) -> bool {
147        self.0.is_empty() && self.1.is_empty()
148    }
149}
150
151/// The error returned when a value that must carry caller-supplied data
152/// turned out to be [empty](MaybeEmpty).
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
154#[error("context value carries no caller-supplied data")]
155pub struct EmptyError;
156
157/// A context value proven to carry caller-supplied bytes.
158///
159/// An AEAD associated-data value and a PRF context can both legitimately be
160/// empty. For some callers, though, an empty context is a security bug rather
161/// than a degenerate case: when one value domain-separates every primitive a
162/// field uses, an empty one collapses that separation — equal plaintexts in
163/// different fields derive identical index terms, every field shares one
164/// derived key, and ciphertexts become transplantable between fields.
165///
166/// `NonEmpty` lets such a caller *demand* non-emptiness in a bound, without
167/// forcing the invariant on anyone who wants an empty context. It follows the
168/// `NonZero` pattern: the check ([`MaybeEmpty`]) happens exactly once, at
169/// construction, and after that the type carries the invariant, so an API can
170/// take a `NonEmpty<C>` instead of re-checking on every use.
171///
172/// `NonEmpty<T>` is transparent to the vitaminc context traits: wrapping a
173/// value changes nothing about how it is encoded, only what the type promises.
174///
175/// It is transparent to `Debug` too: unlike [`Protected`](crate::Protected),
176/// it does not redact, so `{:?}` prints the inner value verbatim. Context
177/// values are normally public identifiers (`"users/email"`), which is why this
178/// is the default — but if a context is derived from sensitive data, wrap it
179/// in a redacting type before proving it non-empty, not after.
180///
181/// # Building one
182///
183/// The rule is: checked once where the type cannot prove non-emptiness,
184/// converted freely where it can.
185///
186/// - **Literals** are checked at compile time with
187///   [`nonempty!`](crate::nonempty); an empty one fails to compile.
188/// - **Dynamic values** are checked at runtime, once, with [`NonEmpty::new`].
189/// - **Integers** are never empty, so `From` converts them with no check:
190///   `NonEmpty::from(7u64)`, `7u64.into()`.
191/// - **A proven value** is extended with [`NonEmpty::with`], which pairs it
192///   with a tail and checks nothing, because the head already carries bytes.
193///
194/// There is no implicit conversion from a string or byte slice: `""` and
195/// `"users/email"` are the same type, so an API accepting a bare `&str`
196/// could only downgrade to a runtime check while appearing to promise more.
197/// An API that requires the invariant therefore takes `NonEmpty<C>` itself,
198/// and the call site states which path it is on:
199///
200/// ```rust
201/// use vitaminc_protected::{nonempty, EmptyError, NonEmpty};
202///
203/// fn bind<C>(context: NonEmpty<C>) -> NonEmpty<C> {
204///     context
205/// }
206///
207/// // A literal: proven non-empty at compile time, no runtime check.
208/// assert_eq!(bind(nonempty!("users/email")).get(), &"users/email");
209///
210/// // A dynamic value: checked structurally, once, at construction.
211/// let field = String::from("users/email");
212/// assert_eq!(bind(NonEmpty::new(field)?).get(), "users/email");
213///
214/// // An integer: never empty, so no check at all.
215/// assert_eq!(bind(NonEmpty::from(7u64)).get(), &7u64);
216///
217/// // A proven head extended with a call-site value: no second check.
218/// assert_eq!(bind(nonempty!("users/email").with(42u64)).get(), &("users/email", 42u64));
219///
220/// // Nesting carries the invariant through.
221/// assert!(NonEmpty::new(("users", Some("email"))).is_ok());
222/// assert_eq!(NonEmpty::new(("", None::<&str>)).unwrap_err(), EmptyError);
223/// # Ok::<(), EmptyError>(())
224/// ```
225///
226/// # Examples
227///
228/// ```rust
229/// use vitaminc_protected::{nonempty, EmptyError, NonEmpty};
230///
231/// // Runtime-checked, for dynamic values.
232/// let field = String::from("users/email");
233/// let context = NonEmpty::new(field)?;
234/// assert_eq!(context.get(), "users/email");
235///
236/// assert_eq!(NonEmpty::new(String::new()).unwrap_err(), EmptyError);
237///
238/// // Compile-time-checked, for literals: `nonempty!("")` does not compile.
239/// let context: NonEmpty<&'static str> = nonempty!("users/email");
240/// assert_eq!(context.into_inner(), "users/email");
241/// # Ok::<(), EmptyError>(())
242/// ```
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244pub struct NonEmpty<T>(T);
245
246impl<T> NonEmpty<T>
247where
248    T: MaybeEmpty,
249{
250    /// Wraps `value`, checking once that it is not [empty](MaybeEmpty).
251    ///
252    /// # Errors
253    ///
254    /// Returns [`EmptyError`] if `value.is_empty()`.
255    #[must_use = "the proof lives in the returned value"]
256    pub fn new(value: T) -> Result<Self, EmptyError> {
257        if value.is_empty() {
258            Err(EmptyError)
259        } else {
260            Ok(Self(value))
261        }
262    }
263}
264
265impl<T> NonEmpty<T> {
266    /// Consumes the wrapper, returning the inner value.
267    pub fn into_inner(self) -> T {
268        self.0
269    }
270
271    /// Borrows the inner value.
272    pub fn get(&self) -> &T {
273        &self.0
274    }
275
276    /// Pairs this proven value with `tail`, keeping the proof and checking
277    /// nothing: a pair is [empty](MaybeEmpty) only when **both** halves are, so
278    /// a head that carries caller bytes makes the pair carry them whatever
279    /// the tail is — `()` or `""` included. This is how a fixed context is
280    /// extended with a value known only at the call site, a record id say,
281    /// without giving up the invariant the head already proved:
282    ///
283    /// ```rust
284    /// use vitaminc_protected::{nonempty, NonEmpty};
285    ///
286    /// let column = nonempty!("users/email");
287    /// let row: NonEmpty<(&str, u64)> = column.with(42u64);
288    /// assert_eq!(row.get(), &("users/email", 42u64));
289    /// ```
290    ///
291    /// Neither side is bounded. The head needs no `MaybeEmpty` because it is
292    /// already proven, so a generic `NonEmpty<C>` extends without `C:
293    /// MaybeEmpty` leaking into the caller's bounds; the tail needs none
294    /// because nothing is evaluated on it. Any type is a valid tail: a
295    /// downstream context type with no `MaybeEmpty` impl, an already-encoded
296    /// `Context`, even another `NonEmpty`. The pair frames it
297    /// once, as the tuple would.
298    ///
299    /// ```rust
300    /// use vitaminc_protected::{nonempty, NonEmpty};
301    ///
302    /// struct RecordId(u64); // implements nothing from this crate
303    ///
304    /// let row: NonEmpty<(&str, RecordId)> = nonempty!("users/email").with(RecordId(7));
305    /// assert_eq!(row.get().1 .0, 7);
306    /// ```
307    ///
308    /// The pair encodes exactly as the bare `(T, U)` would (`NonEmpty` is
309    /// transparent to the context traits), so `nonempty!("users/email")
310    /// .with(42u64)` encodes to the same bytes as `("users/email", 42u64)`.
311    /// Chaining nests to the **left**: `a.with(b).with(c)` is `((a, b), c)`,
312    /// which encodes differently from `(a, (b, c))`. To match an existing
313    /// tuple layout, pass the whole tail at once: `a.with((b, c))`. For a
314    /// layout with the fixed part on the *right*, build the tuple and prove it
315    /// with [`NonEmpty::new`]; `with` only extends rightwards.
316    ///
317    /// Three things the tail does **not** get from the head:
318    ///
319    /// - It is not checked. If an empty tail would be a bug in your domain, an
320    ///   id that must be present say, validate it before pairing; integer ids
321    ///   need no validation because they are never empty.
322    /// - An empty tail does not vanish: `head.with(())` is still a pair and is
323    ///   framed as one, so it does not encode to the same bytes as `head`
324    ///   alone. Do not use an empty tail to mean "absent" — in AEAD `()`, `""`
325    ///   and an empty `Vec<u8>` all frame to the same zero bytes.
326    /// - Its type is not authenticated in AEAD: integers encode as raw
327    ///   little-endian bytes with no tag, so `with(42u64)` and `with(42i64)`
328    ///   produce the same AAD. See the integer note on `IntoAad`.
329    #[must_use = "`with` returns the extended context and leaves the receiver unchanged"]
330    pub fn with<U>(self, tail: U) -> NonEmpty<(T, U)> {
331        NonEmpty((self.0, tail))
332    }
333}
334
335impl NonEmpty<&'static str> {
336    /// Wraps a static string, checking at compile time when evaluated in a
337    /// `const` context. Prefer [`nonempty!`](crate::nonempty), which does
338    /// that for you.
339    ///
340    /// # Panics
341    ///
342    /// Panics if `value` is empty. In the initialiser of a `const` item that
343    /// panic is a compile error, which is the point.
344    /// At runtime it is a real panic, so use [`NonEmpty::new`] for values that
345    /// are not literals.
346    #[must_use = "the proof lives in the returned value"]
347    pub const fn from_static(value: &'static str) -> Self {
348        assert!(!value.is_empty(), "a non-empty context cannot be empty");
349        Self(value)
350    }
351}
352
353impl NonEmpty<&'static [u8]> {
354    /// Wraps a static byte string, checking at compile time when evaluated in
355    /// a `const` context. Prefer [`nonempty_bytes!`](crate::nonempty_bytes),
356    /// which does that for you.
357    ///
358    /// # Panics
359    ///
360    /// Panics if `value` is empty. In the initialiser of a `const` item that
361    /// panic is a compile error, which is the point.
362    /// At runtime it is a real panic, so use [`NonEmpty::new`] for values that
363    /// are not literals.
364    #[must_use = "the proof lives in the returned value"]
365    pub const fn from_static_bytes(value: &'static [u8]) -> Self {
366        assert!(!value.is_empty(), "a non-empty context cannot be empty");
367        Self(value)
368    }
369}
370
371/// A [`NonEmpty<&'static str>`](NonEmpty) checked at compile time.
372///
373/// Expands to a `const` item, so an empty value fails to compile rather
374/// than panicking at runtime. (A `const { … }` block would not do: its
375/// evaluation is deferred to codegen, so `cargo check` — and any tool built
376/// on it — would not report the error.)
377///
378/// Any `&'static str` constant expression works, not just a literal: a
379/// `const`, or a `concat!`/`env!` composition, stays compile-checked because
380/// the check runs in the `const` item the macro expands to. For byte-string
381/// contexts use [`nonempty_bytes!`](crate::nonempty_bytes).
382///
383/// ```rust
384/// use vitaminc_protected::{nonempty, NonEmpty};
385///
386/// let context: NonEmpty<&'static str> = nonempty!("users/email");
387/// assert_eq!(context.get(), &"users/email");
388///
389/// const TABLE: &str = "users";
390/// let composed = nonempty!(concat!("users", "/", "email"));
391/// let from_const = nonempty!(TABLE);
392/// assert_eq!(composed.get(), &"users/email");
393/// assert_eq!(from_const.get(), &"users");
394/// ```
395///
396/// ```rust,compile_fail
397/// let context = vitaminc_protected::nonempty!("");
398/// ```
399#[macro_export]
400macro_rules! nonempty {
401    ($value:expr) => {{
402        const CONTEXT: $crate::NonEmpty<&'static str> = $crate::NonEmpty::from_static($value);
403        CONTEXT
404    }};
405}
406
407/// A [`NonEmpty<&'static [u8]>`](NonEmpty) checked at compile time.
408///
409/// The byte-string twin of [`nonempty!`](crate::nonempty) — the `Context` derivation
410/// APIs are byte-oriented, so a domain tag that is naturally a
411/// byte string deserves the same compile-time path as a `&str` one, instead
412/// of a runtime `NonEmpty::new(b"tag".as_slice()).unwrap()`.
413///
414/// ```rust
415/// use vitaminc_protected::{nonempty_bytes, NonEmpty};
416///
417/// let context: NonEmpty<&'static [u8]> = nonempty_bytes!(b"users/email");
418/// assert_eq!(context.get(), &b"users/email".as_slice());
419/// ```
420///
421/// ```rust,compile_fail
422/// let context = vitaminc_protected::nonempty_bytes!(b"");
423/// ```
424#[macro_export]
425macro_rules! nonempty_bytes {
426    ($value:expr) => {{
427        const CONTEXT: $crate::NonEmpty<&'static [u8]> =
428            $crate::NonEmpty::from_static_bytes($value);
429        CONTEXT
430    }};
431}
432
433#[cfg(test)]
434mod tests {
435    use super::*;
436    use quickcheck_macros::quickcheck;
437
438    #[test]
439    fn empty_shapes_are_empty() {
440        assert!(().is_empty_ctx());
441        assert!("".is_empty_ctx());
442        assert!(String::new().is_empty_ctx());
443        assert!(b"".as_slice().is_empty_ctx());
444        assert!([0u8; 0].is_empty_ctx());
445        assert!(Vec::<u8>::new().is_empty_ctx());
446        assert!(Cow::<[u8]>::Borrowed(&[]).is_empty_ctx());
447        assert!(None::<&str>.is_empty_ctx());
448        assert!(Some("").is_empty_ctx());
449        assert!(("", "").is_empty_ctx());
450        assert!(Some(None::<&str>).is_empty_ctx());
451        assert!((None::<&str>, Some("")).is_empty_ctx());
452        assert!(((), ()).is_empty_ctx());
453    }
454
455    #[test]
456    fn with_pairs_unchecked_and_nests_left() {
457        // The head is proven; the tail is not checked, and need not carry
458        // anything — a pair is empty only when both halves are. Chaining
459        // nests to the left, as the rustdoc promises.
460        let head = nonempty!("users/email");
461        assert_eq!(head.with(()).get(), &("users/email", ()));
462        assert_eq!(head.with("").get(), &("users/email", ""));
463        assert_eq!(
464            head.with(String::from("acme")).with(7u32).into_inner(),
465            (("users/email", String::from("acme")), 7u32)
466        );
467    }
468
469    #[test]
470    fn with_needs_no_bound_on_the_tail() {
471        // #313: any tail, no check. A type with no `MaybeEmpty` impl, an
472        // already-encoded context stand-in, and another `NonEmpty` all pass.
473        // Adding `U: MaybeEmpty` to `with` would stop this compiling.
474        struct Opaque;
475        let head = nonempty!("users/email");
476        let _: NonEmpty<(&str, Opaque)> = head.with(Opaque);
477        let _: NonEmpty<(&str, NonEmpty<&str>)> = head.with(nonempty!("acme"));
478    }
479
480    #[test]
481    #[allow(deprecated)]
482    fn the_old_trait_name_still_resolves() {
483        // `IsEmpty` is a deprecated alias for `MaybeEmpty`: 0.2.0 bounds and
484        // impls keep compiling, with a warning that names the replacement.
485        fn check<T: crate::IsEmpty>(value: &T) -> bool {
486            crate::IsEmpty::is_empty(value)
487        }
488        struct Never;
489        impl crate::IsEmpty for Never {
490            fn is_empty(&self) -> bool {
491                false
492            }
493        }
494        assert!(check(&""));
495        assert!(!check(&Never));
496        assert!(NonEmpty::new(Never).is_ok());
497    }
498
499    #[test]
500    fn with_needs_no_bound_on_the_head() {
501        // The point of `with` for a generic consumer: a `NonEmpty<C>` can be
502        // extended without `C: MaybeEmpty` leaking into the caller's bounds.
503        // Adding `T: MaybeEmpty` to `with` would stop this compiling.
504        fn bind_row<C>(column: NonEmpty<C>, row: u64) -> NonEmpty<(C, u64)> {
505            column.with(row)
506        }
507        assert_eq!(
508            bind_row(nonempty!("users/email"), 42).into_inner(),
509            ("users/email", 42u64)
510        );
511    }
512
513    #[test]
514    fn integers_convert_without_a_check() {
515        assert_eq!(NonEmpty::from(0u8).into_inner(), 0u8);
516        assert_eq!(NonEmpty::from(-1i64).into_inner(), -1i64);
517        let id: NonEmpty<u128> = 7u128.into();
518        assert_eq!(id.get(), &7u128);
519    }
520
521    #[test]
522    fn shapes_carrying_information_are_not_empty() {
523        assert!(!"users/email".is_empty_ctx());
524        assert!(!"x".is_empty_ctx());
525        assert!(!String::from("x").is_empty_ctx());
526        assert!(!b"raw".as_slice().is_empty_ctx());
527        assert!(![1u8].is_empty_ctx());
528        assert!(!vec![1u8].is_empty_ctx());
529        assert!(!Cow::<[u8]>::Owned(vec![1]).is_empty_ctx());
530        assert!(!Some("users/email").is_empty_ctx());
531        assert!(!("users", "email").is_empty_ctx());
532        // A composite that still contributes bytes on one side is not the
533        // degenerate case.
534        assert!(!("", "email").is_empty_ctx());
535        assert!(!("users", "").is_empty_ctx());
536        assert!(!(None::<&str>, "email").is_empty_ctx());
537        assert!(!Some(Some("x")).is_empty_ctx());
538    }
539
540    #[test]
541    fn integers_are_never_empty() {
542        assert!(!0u8.is_empty_ctx());
543        assert!(!0u16.is_empty_ctx());
544        assert!(!0u32.is_empty_ctx());
545        assert!(!0u64.is_empty_ctx());
546        assert!(!0u128.is_empty_ctx());
547        assert!(!0i8.is_empty_ctx());
548        assert!(!0i16.is_empty_ctx());
549        assert!(!0i32.is_empty_ctx());
550        assert!(!0i64.is_empty_ctx());
551        assert!(!0i128.is_empty_ctx());
552        assert!(!7u64.is_empty_ctx());
553        assert!(!Some(0u64).is_empty_ctx());
554    }
555
556    #[test]
557    fn references_defer_to_the_referent() {
558        let owned = String::from("x");
559        assert!(!<&String as MaybeEmpty>::is_empty(&&owned));
560        assert!(!<&&String as MaybeEmpty>::is_empty(&&&owned));
561        let empty = String::new();
562        assert!(<&String as MaybeEmpty>::is_empty(&&empty));
563        assert!(!<&[u8; 3] as MaybeEmpty>::is_empty(&b"abc"));
564        assert!(!<&str as MaybeEmpty>::is_empty(&"abc"));
565    }
566
567    #[test]
568    fn new_checks_once_at_construction() {
569        assert_eq!(NonEmpty::new("").unwrap_err(), EmptyError);
570        assert_eq!(NonEmpty::new(("", "")).unwrap_err(), EmptyError);
571        assert_eq!(NonEmpty::new(None::<&str>).unwrap_err(), EmptyError);
572
573        let context = NonEmpty::new("users/email").unwrap();
574        assert_eq!(context.get(), &"users/email");
575        assert_eq!(context.into_inner(), "users/email");
576
577        let nested = NonEmpty::new(("users", Some("email"))).unwrap();
578        assert_eq!(nested.into_inner(), ("users", Some("email")));
579    }
580
581    #[test]
582    fn from_static_accepts_a_non_empty_literal() {
583        const CONTEXT: NonEmpty<&'static str> = NonEmpty::from_static("users/email");
584        assert_eq!(CONTEXT.get(), &"users/email");
585        assert_eq!(nonempty!("users/email"), CONTEXT);
586    }
587
588    #[test]
589    #[should_panic(expected = "a non-empty context cannot be empty")]
590    fn from_static_panics_on_an_empty_string_at_runtime() {
591        let empty = String::new();
592        // Leak so the value is genuinely `'static` yet not a compile-time
593        // constant; the `const fn` runs at runtime here.
594        let leaked: &'static str = Box::leak(empty.into_boxed_str());
595        let _ = NonEmpty::from_static(leaked);
596    }
597
598    #[test]
599    fn from_static_bytes_accepts_a_non_empty_byte_string() {
600        const CONTEXT: NonEmpty<&'static [u8]> = NonEmpty::from_static_bytes(b"users/email");
601        assert_eq!(CONTEXT.get(), &b"users/email".as_slice());
602        assert_eq!(nonempty_bytes!(b"users/email"), CONTEXT);
603    }
604
605    #[test]
606    #[should_panic(expected = "a non-empty context cannot be empty")]
607    fn from_static_bytes_panics_on_empty_bytes_at_runtime() {
608        let leaked: &'static [u8] = Box::leak(Vec::new().into_boxed_slice());
609        let _ = NonEmpty::from_static_bytes(leaked);
610    }
611
612    #[test]
613    fn nonempty_macro_accepts_any_static_constant_expression() {
614        const TABLE: &str = "users";
615        assert_eq!(nonempty!(TABLE).get(), &"users");
616        assert_eq!(
617            nonempty!(concat!("users", "/", "email")).get(),
618            &"users/email"
619        );
620    }
621
622    #[test]
623    fn cow_is_as_empty_as_its_referent() {
624        assert!(MaybeEmpty::is_empty(&Cow::<str>::Borrowed("")));
625        assert!(MaybeEmpty::is_empty(&Cow::<str>::Owned(String::new())));
626        assert!(!MaybeEmpty::is_empty(&Cow::<str>::Borrowed("x")));
627        assert!(MaybeEmpty::is_empty(&Cow::<[u8]>::Owned(Vec::new())));
628        assert!(!MaybeEmpty::is_empty(&Cow::<[u8]>::Owned(vec![1])));
629    }
630
631    #[test]
632    fn error_is_displayable_and_stable() {
633        assert_eq!(
634            EmptyError.to_string(),
635            "context value carries no caller-supplied data"
636        );
637    }
638
639    #[quickcheck]
640    fn new_succeeds_exactly_when_the_string_has_bytes(value: String) -> bool {
641        NonEmpty::new(value.clone()).is_ok() != value.is_empty()
642    }
643
644    #[quickcheck]
645    fn new_succeeds_exactly_when_the_bytes_are_non_empty(value: Vec<u8>) -> bool {
646        NonEmpty::new(value.clone()).is_ok() != value.is_empty()
647    }
648
649    #[quickcheck]
650    fn option_is_as_empty_as_its_payload(value: Option<String>) -> bool {
651        let expected = value.as_ref().is_none_or(|inner| inner.is_empty());
652        MaybeEmpty::is_empty(&value) == expected
653    }
654
655    #[quickcheck]
656    fn pair_is_empty_only_when_both_sides_are(left: String, right: Vec<u8>) -> bool {
657        let expected = left.is_empty() && right.is_empty();
658        MaybeEmpty::is_empty(&(left, right)) == expected
659    }
660
661    /// Disambiguates from the inherent `is_empty` on `str`, `String`, `Vec`
662    /// and slices so every assertion above exercises the trait.
663    trait MaybeEmptyCtx {
664        fn is_empty_ctx(&self) -> bool;
665    }
666
667    impl<T: MaybeEmpty + ?Sized> MaybeEmptyCtx for T {
668        fn is_empty_ctx(&self) -> bool {
669            MaybeEmpty::is_empty(self)
670        }
671    }
672}