#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::slice::{Iter, IterMut};
use std::vec;
use thiserror::Error;
#[derive(PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct BoundedVec<T, const L: usize, const U: usize>
{
inner: Vec<T>,
}
#[derive(Error, PartialEq, Eq, Debug, Clone)]
pub enum BoundedVecOutOfBounds {
#[error("Lower bound violation: got {got} (expected >= {lower_bound})")]
LowerBoundError {
lower_bound: usize,
got: usize,
},
#[error("Upper bound violation: got {got} (expected <= {upper_bound})")]
UpperBoundError {
upper_bound: usize,
got: usize,
},
}
impl<T, const L: usize, const U: usize> BoundedVec<T, L, U> {
pub fn from_vec(items: Vec<T>) -> Result<Self, BoundedVecOutOfBounds> {
assert!(L > 0);
let len = items.len();
if len < L {
Err(BoundedVecOutOfBounds::LowerBoundError {
lower_bound: L,
got: len,
})
} else if len > U {
Err(BoundedVecOutOfBounds::UpperBoundError {
upper_bound: U,
got: len,
})
} else {
Ok(BoundedVec { inner: items })
}
}
pub fn as_vec(&self) -> &Vec<T> {
&self.inner
}
pub fn to_vec(self) -> Vec<T> {
self.into()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
false
}
pub fn as_slice(&self) -> &[T] {
self.inner.as_slice()
}
pub fn first(&self) -> &T {
#[allow(clippy::unwrap_used)]
self.inner.first().unwrap()
}
pub fn last(&self) -> &T {
#[allow(clippy::unwrap_used)]
self.inner.last().unwrap()
}
pub fn mapped<F, N>(self, map_fn: F) -> BoundedVec<N, L, U>
where
F: FnMut(T) -> N,
{
BoundedVec {
inner: self.inner.into_iter().map(map_fn).collect::<Vec<_>>(),
}
}
pub fn mapped_ref<F, N>(&self, map_fn: F) -> BoundedVec<N, L, U>
where
F: FnMut(&T) -> N,
{
BoundedVec {
inner: self.inner.iter().map(map_fn).collect::<Vec<_>>(),
}
}
pub fn try_mapped<F, N, E>(self, map_fn: F) -> Result<BoundedVec<N, L, U>, E>
where
F: FnMut(T) -> Result<N, E>,
{
let mut map_fn = map_fn;
let mut out = Vec::with_capacity(self.len());
for element in self.inner.into_iter() {
out.push(map_fn(element)?);
}
#[allow(clippy::unwrap_used)]
Ok(BoundedVec::from_vec(out).unwrap())
}
pub fn try_mapped_ref<F, N, E>(&self, map_fn: F) -> Result<BoundedVec<N, L, U>, E>
where
F: FnMut(&T) -> Result<N, E>,
{
let mut map_fn = map_fn;
let mut out = Vec::with_capacity(self.len());
for element in self.inner.iter() {
out.push(map_fn(element)?);
}
#[allow(clippy::unwrap_used)]
Ok(BoundedVec::from_vec(out).unwrap())
}
pub fn get(&self, index: usize) -> Option<&T> {
self.inner.get(index)
}
pub fn iter(&self) -> Iter<T> {
self.inner.iter()
}
pub fn iter_mut(&mut self) -> IterMut<T> {
self.inner.iter_mut()
}
pub fn split_last(&self) -> (&T, &[T]) {
#[allow(clippy::unwrap_used)]
self.inner.split_last().unwrap()
}
pub fn enumerated(self) -> BoundedVec<(usize, T), L, U> {
#[allow(clippy::unwrap_used)]
self.inner
.into_iter()
.enumerate()
.collect::<Vec<(usize, T)>>()
.try_into()
.unwrap()
}
pub fn opt_empty_vec(v: Vec<T>) -> Result<Option<BoundedVec<T, L, U>>, BoundedVecOutOfBounds> {
if v.is_empty() {
Ok(None)
} else {
Ok(Some(BoundedVec::from_vec(v)?))
}
}
}
pub type NonEmptyVec<T> = BoundedVec<T, 1, { usize::MAX }>;
impl<T, const L: usize, const U: usize> TryFrom<Vec<T>> for BoundedVec<T, L, U> {
type Error = BoundedVecOutOfBounds;
fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
BoundedVec::from_vec(value)
}
}
impl<T, const L: usize, const U: usize> From<[T; L]> for BoundedVec<T, L, U> {
fn from(arr: [T; L]) -> Self {
BoundedVec { inner: arr.into() }
}
}
impl<T, const L: usize, const U: usize> From<BoundedVec<T, L, U>> for Vec<T> {
fn from(v: BoundedVec<T, L, U>) -> Self {
v.inner
}
}
impl<T, const L: usize, const U: usize> IntoIterator for BoundedVec<T, L, U> {
type Item = T;
type IntoIter = vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a, T, const L: usize, const U: usize> IntoIterator for &'a BoundedVec<T, L, U> {
type Item = &'a T;
type IntoIter = core::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
(&self.inner).iter()
}
}
impl<'a, T, const L: usize, const U: usize> IntoIterator for &'a mut BoundedVec<T, L, U> {
type Item = &'a mut T;
type IntoIter = core::slice::IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
(&mut self.inner).iter_mut()
}
}
impl<T, const L: usize, const U: usize> AsRef<Vec<T>> for BoundedVec<T, L, U> {
fn as_ref(&self) -> &Vec<T> {
&self.inner
}
}
impl<T, const L: usize, const U: usize> AsRef<[T]> for BoundedVec<T, L, U> {
fn as_ref(&self) -> &[T] {
self.inner.as_ref()
}
}
impl<T, const L: usize, const U: usize> AsMut<Vec<T>> for BoundedVec<T, L, U> {
fn as_mut(&mut self) -> &mut Vec<T> {
self.inner.as_mut()
}
}
impl<T, const L: usize, const U: usize> AsMut<[T]> for BoundedVec<T, L, U> {
fn as_mut(&mut self) -> &mut [T] {
self.inner.as_mut()
}
}
pub trait OptBoundedVecToVec<T> {
fn to_vec(self) -> Vec<T>;
}
impl<T, const L: usize, const U: usize> OptBoundedVecToVec<T> for Option<BoundedVec<T, L, U>> {
fn to_vec(self) -> Vec<T> {
self.map(|bv| bv.into()).unwrap_or_default()
}
}
#[allow(clippy::unwrap_used)]
#[cfg(feature = "arbitrary")]
mod arbitrary {
use super::*;
use proptest::collection::vec;
use proptest::prelude::Arbitrary;
use proptest::prelude::*;
use proptest::strategy::BoxedStrategy;
impl<T: Arbitrary, const L: usize, const U: usize> Arbitrary for BoundedVec<T, L, U>
where
T::Strategy: 'static,
{
type Strategy = BoxedStrategy<Self>;
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
vec(any::<T>(), L..=U)
.prop_map(|items| BoundedVec::from_vec(items).unwrap())
.boxed()
}
}
}
#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use std::convert::TryInto;
use super::*;
#[test]
fn from_vec() {
assert!(BoundedVec::<u8, 2, 8>::from_vec(vec![1, 2]).is_ok());
assert!(BoundedVec::<u8, 2, 8>::from_vec(vec![]).is_err());
assert!(BoundedVec::<u8, 3, 8>::from_vec(vec![1, 2]).is_err());
assert!(BoundedVec::<u8, 1, 2>::from_vec(vec![1, 2, 3]).is_err());
}
#[test]
fn is_empty() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert!(!data.is_empty());
}
#[test]
fn as_vec() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert_eq!(data.as_vec(), &vec![1u8, 2]);
}
#[test]
fn as_slice() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert_eq!(data.as_slice(), &[1u8, 2]);
}
#[test]
fn len() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert_eq!(data.len(), 2);
}
#[test]
fn first() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert_eq!(data.first(), &1u8);
}
#[test]
fn last() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert_eq!(data.last(), &2u8);
}
#[test]
fn mapped() {
let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
let data = data.mapped(|x| x * 2);
assert_eq!(data, [2u8, 4].into());
}
#[test]
fn mapped_ref() {
let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
let data = data.mapped_ref(|x| x * 2);
assert_eq!(data, [2u8, 4].into());
}
#[test]
fn get() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert_eq!(data.get(1).unwrap(), &2u8);
assert!(data.get(3).is_none());
}
#[test]
fn try_mapped() {
let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
let data = data.try_mapped(|x| 100u8.checked_div(x).ok_or("error"));
assert_eq!(data, Ok([100u8, 50].into()));
}
#[test]
fn try_mapped_error() {
let data: BoundedVec<u8, 2, 8> = [0u8, 2].into();
let data = data.try_mapped(|x| 100u8.checked_div(x).ok_or("error"));
assert_eq!(data, Err("error"));
}
#[test]
fn try_mapped_ref() {
let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
let data = data.try_mapped_ref(|x| 100u8.checked_div(*x).ok_or("error"));
assert_eq!(data, Ok([100u8, 50].into()));
}
#[test]
fn try_mapped_ref_error() {
let data: BoundedVec<u8, 2, 8> = [0u8, 2].into();
let data = data.try_mapped_ref(|x| 100u8.checked_div(*x).ok_or("error"));
assert_eq!(data, Err("error"));
}
#[test]
fn split_last() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
assert_eq!(data.split_last(), (&2u8, [1u8].as_ref()));
let data1: BoundedVec<_, 1, 8> = vec![1u8].try_into().unwrap();
assert_eq!(data1.split_last(), (&1u8, Vec::new().as_ref()));
}
#[test]
fn enumerated() {
let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
let expected: BoundedVec<_, 2, 8> = vec![(0, 1u8), (1, 2)].try_into().unwrap();
assert_eq!(data.enumerated(), expected);
}
#[test]
fn into_iter() {
let mut vec = vec![1u8, 2];
let mut data: BoundedVec<_, 2, 8> = vec.clone().try_into().unwrap();
assert_eq!(data.clone().into_iter().collect::<Vec<u8>>(), vec);
assert_eq!(
data.iter().collect::<Vec<&u8>>(),
vec.iter().collect::<Vec<&u8>>()
);
assert_eq!(
data.iter_mut().collect::<Vec<&mut u8>>(),
vec.iter_mut().collect::<Vec<&mut u8>>()
);
}
}
#[cfg(feature = "arbitrary")]
#[cfg(test)]
#[allow(clippy::len_zero)]
mod arb_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn bounded_vec_length_bounded(v: BoundedVec<u8, 1, 2>) {
prop_assert!(1 <= v.len() && v.len() <= 2);
}
}
}