Skip to main content

NonEmpty

Struct NonEmpty 

Source
pub struct NonEmpty<T>(/* private fields */);
Expand description

A context value proven to carry caller-supplied bytes.

An AEAD associated-data value and a PRF context can both legitimately be empty. For some callers, though, an empty context is a security bug rather than a degenerate case: when one value domain-separates every primitive a field uses, an empty one collapses that separation — equal plaintexts in different fields derive identical index terms, every field shares one derived key, and ciphertexts become transplantable between fields.

NonEmpty lets such a caller demand non-emptiness in a bound, without forcing the invariant on anyone who wants an empty context. It follows the NonZero pattern: the check (MaybeEmpty) happens exactly once, at construction, and after that the type carries the invariant, so an API can take a NonEmpty<C> instead of re-checking on every use.

NonEmpty<T> is transparent to the vitaminc context traits: wrapping a value changes nothing about how it is encoded, only what the type promises.

It is transparent to Debug too: unlike Protected, it does not redact, so {:?} prints the inner value verbatim. Context values are normally public identifiers ("users/email"), which is why this is the default — but if a context is derived from sensitive data, wrap it in a redacting type before proving it non-empty, not after.

§Building one

The rule is: checked once where the type cannot prove non-emptiness, converted freely where it can.

  • Literals are checked at compile time with nonempty!; an empty one fails to compile.
  • Dynamic values are checked at runtime, once, with NonEmpty::new.
  • Integers are never empty, so From converts them with no check: NonEmpty::from(7u64), 7u64.into().
  • A proven value is extended with NonEmpty::with, which pairs it with a tail and checks nothing, because the head already carries bytes.

There is no implicit conversion from a string or byte slice: "" and "users/email" are the same type, so an API accepting a bare &str could only downgrade to a runtime check while appearing to promise more. An API that requires the invariant therefore takes NonEmpty<C> itself, and the call site states which path it is on:

use vitaminc_protected::{nonempty, EmptyError, NonEmpty};

fn bind<C>(context: NonEmpty<C>) -> NonEmpty<C> {
    context
}

// A literal: proven non-empty at compile time, no runtime check.
assert_eq!(bind(nonempty!("users/email")).get(), &"users/email");

// A dynamic value: checked structurally, once, at construction.
let field = String::from("users/email");
assert_eq!(bind(NonEmpty::new(field)?).get(), "users/email");

// An integer: never empty, so no check at all.
assert_eq!(bind(NonEmpty::from(7u64)).get(), &7u64);

// A proven head extended with a call-site value: no second check.
assert_eq!(bind(nonempty!("users/email").with(42u64)).get(), &("users/email", 42u64));

// Nesting carries the invariant through.
assert!(NonEmpty::new(("users", Some("email"))).is_ok());
assert_eq!(NonEmpty::new(("", None::<&str>)).unwrap_err(), EmptyError);

§Examples

use vitaminc_protected::{nonempty, EmptyError, NonEmpty};

// Runtime-checked, for dynamic values.
let field = String::from("users/email");
let context = NonEmpty::new(field)?;
assert_eq!(context.get(), "users/email");

assert_eq!(NonEmpty::new(String::new()).unwrap_err(), EmptyError);

// Compile-time-checked, for literals: `nonempty!("")` does not compile.
let context: NonEmpty<&'static str> = nonempty!("users/email");
assert_eq!(context.into_inner(), "users/email");

Implementations§

Source§

impl<T> NonEmpty<T>
where T: MaybeEmpty,

Source

pub fn new(value: T) -> Result<Self, EmptyError>

Wraps value, checking once that it is not empty.

§Errors

Returns EmptyError if value.is_empty().

Source§

impl<T> NonEmpty<T>

Source

pub fn into_inner(self) -> T

Consumes the wrapper, returning the inner value.

Source

pub fn get(&self) -> &T

Borrows the inner value.

Source

pub fn with<U>(self, tail: U) -> NonEmpty<(T, U)>

Pairs this proven value with tail, keeping the proof and checking nothing: a pair is empty only when both halves are, so a head that carries caller bytes makes the pair carry them whatever the tail is — () or "" included. This is how a fixed context is extended with a value known only at the call site, a record id say, without giving up the invariant the head already proved:

use vitaminc_protected::{nonempty, NonEmpty};

let column = nonempty!("users/email");
let row: NonEmpty<(&str, u64)> = column.with(42u64);
assert_eq!(row.get(), &("users/email", 42u64));

Neither side is bounded. The head needs no MaybeEmpty because it is already proven, so a generic NonEmpty<C> extends without C: MaybeEmpty leaking into the caller’s bounds; the tail needs none because nothing is evaluated on it. Any type is a valid tail: a downstream context type with no MaybeEmpty impl, an already-encoded Context, even another NonEmpty. The pair frames it once, as the tuple would.

use vitaminc_protected::{nonempty, NonEmpty};

struct RecordId(u64); // implements nothing from this crate

let row: NonEmpty<(&str, RecordId)> = nonempty!("users/email").with(RecordId(7));
assert_eq!(row.get().1 .0, 7);

The pair encodes exactly as the bare (T, U) would (NonEmpty is transparent to the context traits), so nonempty!("users/email") .with(42u64) encodes to the same bytes as ("users/email", 42u64). Chaining nests to the left: a.with(b).with(c) is ((a, b), c), which encodes differently from (a, (b, c)). To match an existing tuple layout, pass the whole tail at once: a.with((b, c)). For a layout with the fixed part on the right, build the tuple and prove it with NonEmpty::new; with only extends rightwards.

Three things the tail does not get from the head:

  • It is not checked. If an empty tail would be a bug in your domain, an id that must be present say, validate it before pairing; integer ids need no validation because they are never empty.
  • An empty tail does not vanish: head.with(()) is still a pair and is framed as one, so it does not encode to the same bytes as head alone. Do not use an empty tail to mean “absent” — in AEAD (), "" and an empty Vec<u8> all frame to the same zero bytes.
  • Its type is not authenticated in AEAD: integers encode as raw little-endian bytes with no tag, so with(42u64) and with(42i64) produce the same AAD. See the integer note on IntoAad.
Source§

impl NonEmpty<&'static str>

Source

pub const fn from_static(value: &'static str) -> Self

Wraps a static string, checking at compile time when evaluated in a const context. Prefer nonempty!, which does that for you.

§Panics

Panics if value is empty. In the initialiser of a const item that panic is a compile error, which is the point. At runtime it is a real panic, so use NonEmpty::new for values that are not literals.

Source§

impl NonEmpty<&'static [u8]>

Source

pub const fn from_static_bytes(value: &'static [u8]) -> Self

Wraps a static byte string, checking at compile time when evaluated in a const context. Prefer nonempty_bytes!, which does that for you.

§Panics

Panics if value is empty. In the initialiser of a const item that panic is a compile error, which is the point. At runtime it is a real panic, so use NonEmpty::new for values that are not literals.

Trait Implementations§

Source§

impl<T: Clone> Clone for NonEmpty<T>

Source§

fn clone(&self) -> NonEmpty<T>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Copy> Copy for NonEmpty<T>

Source§

impl<T: Debug> Debug for NonEmpty<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Eq> Eq for NonEmpty<T>

Source§

impl From<i8> for NonEmpty<i8>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: i8) -> Self

Converts to this type from the input type.
Source§

impl From<i16> for NonEmpty<i16>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: i16) -> Self

Converts to this type from the input type.
Source§

impl From<i32> for NonEmpty<i32>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: i32) -> Self

Converts to this type from the input type.
Source§

impl From<i64> for NonEmpty<i64>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: i64) -> Self

Converts to this type from the input type.
Source§

impl From<i128> for NonEmpty<i128>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: i128) -> Self

Converts to this type from the input type.
Source§

impl From<u8> for NonEmpty<u8>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: u8) -> Self

Converts to this type from the input type.
Source§

impl From<u16> for NonEmpty<u16>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: u16) -> Self

Converts to this type from the input type.
Source§

impl From<u32> for NonEmpty<u32>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: u32) -> Self

Converts to this type from the input type.
Source§

impl From<u64> for NonEmpty<u64>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: u64) -> Self

Converts to this type from the input type.
Source§

impl From<u128> for NonEmpty<u128>

An integer is never empty, so it converts without a check — NonEmpty::from(7u64) or 7u64.into() — where a string would need NonEmpty::new or nonempty!.

Source§

fn from(value: u128) -> Self

Converts to this type from the input type.
Source§

impl<T: Hash> Hash for NonEmpty<T>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T: PartialEq> PartialEq for NonEmpty<T>

Source§

fn eq(&self, other: &NonEmpty<T>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<T: PartialEq> StructuralPartialEq for NonEmpty<T>

Auto Trait Implementations§

§

impl<T> Freeze for NonEmpty<T>
where T: Freeze,

§

impl<T> RefUnwindSafe for NonEmpty<T>
where T: RefUnwindSafe,

§

impl<T> Send for NonEmpty<T>
where T: Send,

§

impl<T> Sync for NonEmpty<T>
where T: Sync,

§

impl<T> Unpin for NonEmpty<T>
where T: Unpin,

§

impl<T> UnsafeUnpin for NonEmpty<T>
where T: UnsafeUnpin,

§

impl<T> UnwindSafe for NonEmpty<T>
where T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.