277 lines
7.3 KiB
Rust
277 lines
7.3 KiB
Rust
//! Serialization constraint helpers.
|
|
|
|
use std::{
|
|
convert::{TryFrom, TryInto},
|
|
ops::Deref,
|
|
};
|
|
|
|
use crate::serialization::SerializationError;
|
|
|
|
/// A `Vec<T>` wrapper that ensures there is at least one `T` in the vector.
|
|
///
|
|
/// You can initialize `AtLeastOne` using:
|
|
/// ```
|
|
/// # use zebra_chain::serialization::{AtLeastOne, SerializationError};
|
|
/// # use std::convert::{TryFrom, TryInto};
|
|
/// #
|
|
/// let v: AtLeastOne<u32> = vec![42].try_into()?;
|
|
/// assert_eq!(v.as_slice(), [42]);
|
|
///
|
|
/// let v: AtLeastOne<u32> = vec![42].as_slice().try_into()?;
|
|
/// assert_eq!(v.as_slice(), [42]);
|
|
///
|
|
/// let v: AtLeastOne<u32> = [42].try_into()?;
|
|
/// assert_eq!(v.as_slice(), [42]);
|
|
///
|
|
/// let v = AtLeastOne::<u32>::try_from(&[42])?;
|
|
/// assert_eq!(v.as_slice(), [42]);
|
|
/// #
|
|
/// # Ok::<(), SerializationError>(())
|
|
/// ```
|
|
///
|
|
/// And access the inner vector via [deref coercion](https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-deref-coercion),
|
|
/// an explicit conversion, or as a slice:
|
|
/// ```
|
|
/// # use zebra_chain::serialization::AtLeastOne;
|
|
/// # use std::convert::TryInto;
|
|
/// #
|
|
/// # let v: AtLeastOne<u32> = vec![42].try_into().unwrap();
|
|
/// #
|
|
/// let first = v.iter().next().expect("AtLeastOne always has a first element");
|
|
/// assert_eq!(*first, 42);
|
|
///
|
|
/// let s = v.as_slice();
|
|
/// #
|
|
/// # assert_eq!(s, [42]);
|
|
///
|
|
/// let mut m = v.into_vec();
|
|
/// #
|
|
/// # assert_eq!(m.as_slice(), [42]);
|
|
///
|
|
/// ```
|
|
///
|
|
/// `AtLeastOne` also re-implements some slice methods with different return
|
|
/// types, to avoid redundant unwraps:
|
|
/// ```
|
|
/// # use zebra_chain::serialization::AtLeastOne;
|
|
/// # use std::convert::TryInto;
|
|
/// #
|
|
/// # let v: AtLeastOne<u32> = vec![42].try_into().unwrap();
|
|
/// #
|
|
/// let first = v.first();
|
|
/// assert_eq!(*first, 42);
|
|
///
|
|
/// let (first, rest) = v.split_first();
|
|
/// assert_eq!(*first, 42);
|
|
/// assert!(rest.is_empty());
|
|
/// ```
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
pub struct AtLeastOne<T> {
|
|
/// The inner vector, which must have at least one element.
|
|
///
|
|
/// `inner` is private, so that it can't be modified in ways that break the
|
|
/// type constraint.
|
|
inner: Vec<T>,
|
|
}
|
|
|
|
// CORRECTNESS
|
|
//
|
|
// All conversions to `AtLeastOne<T>` must go through `TryFrom<Vec<T>>`,
|
|
// so that the type constraint is satisfied.
|
|
|
|
impl<T> TryFrom<Vec<T>> for AtLeastOne<T> {
|
|
type Error = SerializationError;
|
|
|
|
fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {
|
|
if vec.is_empty() {
|
|
Err(SerializationError::Parse("expected at least one item"))
|
|
} else {
|
|
Ok(AtLeastOne { inner: vec })
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<&Vec<T>> for AtLeastOne<T>
|
|
where
|
|
T: Clone,
|
|
{
|
|
type Error = SerializationError;
|
|
|
|
fn try_from(vec: &Vec<T>) -> Result<Self, Self::Error> {
|
|
if vec.is_empty() {
|
|
Err(SerializationError::Parse("expected at least one item"))
|
|
} else {
|
|
Ok(AtLeastOne {
|
|
inner: vec.to_vec(),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<&[T]> for AtLeastOne<T>
|
|
where
|
|
T: Clone,
|
|
{
|
|
type Error = SerializationError;
|
|
|
|
fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
|
|
slice.to_vec().try_into()
|
|
}
|
|
}
|
|
|
|
// TODO:
|
|
// - reject [T; 0] at compile time and impl From instead?
|
|
impl<T, const N: usize> TryFrom<[T; N]> for AtLeastOne<T>
|
|
where
|
|
T: Clone,
|
|
{
|
|
type Error = SerializationError;
|
|
|
|
fn try_from(slice: [T; N]) -> Result<Self, Self::Error> {
|
|
slice.to_vec().try_into()
|
|
}
|
|
}
|
|
|
|
// TODO:
|
|
// - reject [T; 0] at compile time and impl From instead?
|
|
// - remove when std is updated so that `TryFrom<&U>` is always implemented when
|
|
// `TryFrom<U>`
|
|
impl<T, const N: usize> TryFrom<&[T; N]> for AtLeastOne<T>
|
|
where
|
|
T: Clone,
|
|
{
|
|
type Error = SerializationError;
|
|
|
|
fn try_from(slice: &[T; N]) -> Result<Self, Self::Error> {
|
|
slice.to_vec().try_into()
|
|
}
|
|
}
|
|
|
|
// Deref and AsRef (but not DerefMut or AsMut, because that could break the constraint)
|
|
|
|
impl<T> Deref for AtLeastOne<T> {
|
|
type Target = Vec<T>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.inner
|
|
}
|
|
}
|
|
|
|
impl<T> AsRef<[T]> for AtLeastOne<T> {
|
|
fn as_ref(&self) -> &[T] {
|
|
self.inner.as_ref()
|
|
}
|
|
}
|
|
|
|
// Extracting one or more items
|
|
|
|
impl<T> From<AtLeastOne<T>> for Vec<T> {
|
|
fn from(vec1: AtLeastOne<T>) -> Self {
|
|
vec1.inner
|
|
}
|
|
}
|
|
|
|
// `IntoIterator` for `T` and `&mut T`, because iterators can't remove items
|
|
|
|
impl<T> IntoIterator for AtLeastOne<T> {
|
|
type Item = T;
|
|
|
|
type IntoIter = std::vec::IntoIter<T>;
|
|
|
|
fn into_iter(self) -> std::vec::IntoIter<T> {
|
|
self.inner.into_iter()
|
|
}
|
|
}
|
|
|
|
impl<T> AtLeastOne<T> {
|
|
/// Returns an iterator that allows modifying each value.
|
|
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
|
|
self.inner.iter_mut()
|
|
}
|
|
}
|
|
|
|
impl<T> AtLeastOne<T> {
|
|
/// Returns a new `AtLeastOne`, containing a single `item`.
|
|
///
|
|
/// Skips the `TrustedPreallocate` memory denial of service checks.
|
|
/// (`TrustedPreallocate` can not defend against a single item
|
|
/// that causes a denial of service by itself.)
|
|
pub fn from_one(item: T) -> AtLeastOne<T> {
|
|
AtLeastOne { inner: vec![item] }
|
|
}
|
|
|
|
/// Returns a reference to the inner vector.
|
|
pub fn as_vec(&self) -> &Vec<T> {
|
|
&self.inner
|
|
}
|
|
|
|
/// Converts `self` into a vector without clones or allocation.
|
|
///
|
|
/// The resulting vector can be converted back into `AtLeastOne` via `try_into`.
|
|
pub fn into_vec(self) -> Vec<T> {
|
|
self.inner
|
|
}
|
|
|
|
/// Returns the first element.
|
|
///
|
|
/// Unlike `Vec` or slice, `AtLeastOne` always has a first element.
|
|
pub fn first(&self) -> &T {
|
|
&self.inner[0]
|
|
}
|
|
|
|
/// Returns a mutable reference to the first element.
|
|
///
|
|
/// Unlike `Vec` or slice, `AtLeastOne` always has a first element.
|
|
pub fn first_mut(&mut self) -> &mut T {
|
|
&mut self.inner[0]
|
|
}
|
|
|
|
/// Appends an element to the back of the collection.
|
|
pub fn push(&mut self, element: T) {
|
|
self.inner.push(element);
|
|
}
|
|
|
|
/// Returns the first and all the rest of the elements of the vector.
|
|
///
|
|
/// Unlike `Vec` or slice, `AtLeastOne` always has a first element.
|
|
pub fn split_first(&self) -> (&T, &[T]) {
|
|
(&self.inner[0], &self.inner[1..])
|
|
}
|
|
}
|
|
|
|
// TODO: consider implementing `push`, `append`, and `Extend`,
|
|
// because adding elements can't break the constraint.
|
|
|
|
/// Create an initialized [`AtLeastOne`] instance.
|
|
///
|
|
/// This macro is similar to the [`vec!`][`std::vec!`] macro, but doesn't support creating an empty
|
|
/// `AtLeastOne` instance.
|
|
///
|
|
/// # Security
|
|
///
|
|
/// This macro must only be used in tests, because it skips the `TrustedPreallocate` memory
|
|
/// denial of service checks.
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
#[macro_export]
|
|
macro_rules! at_least_one {
|
|
($element:expr; 0) => (
|
|
compile_error!("At least one element needed to create an `AtLeastOne<T>`")
|
|
);
|
|
|
|
($element:expr; $count:expr) => (
|
|
{
|
|
<Vec<_> as std::convert::TryInto<$crate::serialization::AtLeastOne<_>>>::try_into(
|
|
vec![$element; $expr],
|
|
).expect("at least one element in `AtLeastOne<_>`")
|
|
}
|
|
);
|
|
|
|
($($element:expr),+ $(,)?) => (
|
|
{
|
|
<Vec<_> as std::convert::TryInto<$crate::serialization::AtLeastOne<_>>>::try_into(
|
|
vec![$($element),*],
|
|
).expect("at least one element in `AtLeastOne<_>`")
|
|
}
|
|
);
|
|
}
|