use alloc::vec;
use alloc::vec::Vec;
use core::convert::{TryFrom, TryInto};
use core::slice::{Iter, IterMut};
use thiserror::Error;
#[derive(PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
pub struct BoundedVec<T, const L: usize, const U: usize, W = witnesses::NonEmpty<L, U>> {
inner: Vec<T>,
witness: W,
}
#[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,
},
}
pub mod witnesses {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NonEmpty<const L: usize, const U: usize>(());
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Empty<const U: usize>(());
pub const fn non_empty<const L: usize, const U: usize>() -> NonEmpty<L, U> {
const {
if L == 0 {
panic!("L must be greater than 0")
}
if L > U {
panic!("L must be less than or equal to U")
}
NonEmpty::<L, U>(())
}
}
pub const fn empty<const U: usize>() -> Empty<U> {
const { Empty::<U>(()) }
}
}
impl<T, const U: usize> BoundedVec<T, 0, U, witnesses::Empty<U>> {
pub fn from_vec(items: Vec<T>) -> Result<Self, BoundedVecOutOfBounds> {
let witness = witnesses::empty::<U>();
let len = items.len();
if len > U {
Err(BoundedVecOutOfBounds::UpperBoundError {
upper_bound: U,
got: len,
})
} else {
Ok(BoundedVec {
inner: items,
witness,
})
}
}
pub fn first(&self) -> Option<&T> {
self.inner.first()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn last(&self) -> Option<&T> {
self.inner.last()
}
}
impl<T, const L: usize, const U: usize, W> BoundedVec<T, L, U, W> {
pub fn as_vec(&self) -> &Vec<T> {
&self.inner
}
pub fn to_vec(self) -> Vec<T> {
self.inner
}
pub fn as_slice(&self) -> &[T] {
self.inner.as_slice()
}
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()
}
}
impl<T, const L: usize, const U: usize> BoundedVec<T, L, U, witnesses::NonEmpty<L, U>> {
pub fn from_vec(items: Vec<T>) -> Result<Self, BoundedVecOutOfBounds> {
let witness = witnesses::non_empty::<L, U>();
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,
witness,
})
}
}
pub fn len(&self) -> usize {
self.inner.len()
}
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, witnesses::NonEmpty<L, U>>
where
F: FnMut(T) -> N,
{
BoundedVec {
inner: self.inner.into_iter().map(map_fn).collect::<Vec<_>>(),
witness: self.witness,
}
}
pub fn mapped_ref<F, N>(&self, map_fn: F) -> BoundedVec<N, L, U, witnesses::NonEmpty<L, U>>
where
F: FnMut(&T) -> N,
{
BoundedVec {
inner: self.inner.iter().map(map_fn).collect::<Vec<_>>(),
witness: self.witness,
}
}
pub fn try_mapped<F, N, E>(
self,
map_fn: F,
) -> Result<BoundedVec<N, L, U, witnesses::NonEmpty<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::<N, L, U, witnesses::NonEmpty<L, U>>::from_vec(out).unwrap())
}
pub fn try_mapped_ref<F, N, E>(
&self,
map_fn: F,
) -> Result<BoundedVec<N, L, U, witnesses::NonEmpty<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::<N, L, U, witnesses::NonEmpty<L, U>>::from_vec(out).unwrap())
}
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, witnesses::NonEmpty<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, witnesses::NonEmpty<L, U>>>, BoundedVecOutOfBounds> {
if v.is_empty() {
Ok(None)
} else {
Ok(Some(Self::from_vec(v)?))
}
}
}
pub type NonEmptyVec<T> = BoundedVec<T, 1, { usize::MAX }, witnesses::NonEmpty<1, { usize::MAX }>>;
pub type EmptyBoundedVec<T, const U: usize> = BoundedVec<T, 0, U, witnesses::Empty<U>>;
pub type NonEmptyBoundedVec<T, const L: usize, const U: usize> =
BoundedVec<T, L, U, witnesses::NonEmpty<L, U>>;
impl<T, const L: usize, const U: usize> TryFrom<Vec<T>>
for BoundedVec<T, L, U, witnesses::NonEmpty<L, U>>
{
type Error = BoundedVecOutOfBounds;
fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
Self::from_vec(value)
}
}
impl<T, const U: usize> TryFrom<Vec<T>> for BoundedVec<T, 0, U, witnesses::Empty<U>> {
type Error = BoundedVecOutOfBounds;
fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
Self::from_vec(value)
}
}
impl<T, const L: usize, const U: usize> From<[T; L]>
for BoundedVec<T, L, U, witnesses::NonEmpty<L, U>>
{
fn from(arr: [T; L]) -> Self {
BoundedVec {
inner: arr.into(),
witness: witnesses::non_empty(),
}
}
}
impl<T, const L: usize, const U: usize> From<BoundedVec<T, L, U, witnesses::NonEmpty<L, U>>>
for Vec<T>
{
fn from(v: BoundedVec<T, L, U, witnesses::NonEmpty<L, U>>) -> Self {
v.inner
}
}
impl<T, const L: usize, const U: usize, W> IntoIterator for BoundedVec<T, L, U, W> {
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, W> IntoIterator for &'a BoundedVec<T, L, U, W> {
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, W> IntoIterator for &'a mut BoundedVec<T, L, U, W> {
type Item = &'a mut T;
type IntoIter = core::slice::IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter_mut()
}
}
impl<T, const L: usize, const U: usize, W> AsRef<Vec<T>> for BoundedVec<T, L, U, W> {
fn as_ref(&self) -> &Vec<T> {
&self.inner
}
}
impl<T, const L: usize, const U: usize, W> AsRef<[T]> for BoundedVec<T, L, U, W> {
fn as_ref(&self) -> &[T] {
self.inner.as_ref()
}
}
impl<T, const L: usize, const U: usize, W> AsMut<Vec<T>> for BoundedVec<T, L, U, W> {
fn as_mut(&mut self) -> &mut Vec<T> {
self.inner.as_mut()
}
}
impl<T, const L: usize, const U: usize, W> AsMut<[T]> for BoundedVec<T, L, U, W> {
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, witnesses::NonEmpty<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, witnesses::NonEmpty<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| Self::from_vec(items).unwrap())
.boxed()
}
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::*;
use serde::{Deserialize, Serialize};
impl<T: Serialize, const L: usize, const U: usize, W> Serialize for BoundedVec<T, L, U, W> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner.serialize(serializer)
}
}
impl<'de, T: Deserialize<'de>, const L: usize, const U: usize> Deserialize<'de>
for BoundedVec<T, L, U>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let inner = Vec::<T>::deserialize(deserializer)?;
BoundedVec::<T, L, U>::from_vec(inner).map_err(serde::de::Error::custom)
}
}
impl<'de, T: Deserialize<'de>, const U: usize> Deserialize<'de> for EmptyBoundedVec<T, U> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let inner = Vec::<T>::deserialize(deserializer)?;
EmptyBoundedVec::from_vec(inner).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "schema")]
mod schema {
use super::*;
use alloc::borrow::Cow;
use schemars::JsonSchema;
impl<T: JsonSchema, const L: usize, const U: usize, W> JsonSchema for BoundedVec<T, L, U, W> {
fn schema_name() -> Cow<'static, str> {
alloc::format!("BoundedVec{}Min{}Max{}", T::schema_name(), L, U).into()
}
fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "array",
"items": T::json_schema(gen),
"minItems": L as u32,
"maxItems": U as u32
})
}
}
}
}
#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use core::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: EmptyBoundedVec<_, 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 = "serde")]
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod serde_tests {
use super::*;
use alloc::vec;
#[test]
fn deserialize_nonempty() {
assert_eq!(
serde_json::from_str::<BoundedVec::<u8, 2, 3>>("[1, 2]")
.unwrap()
.as_vec(),
&vec![1, 2]
);
}
#[test]
fn deserialize_empty() {
assert!(serde_json::from_str::<BoundedVec::<u8, 2, 3>>("[]").is_err());
assert!(serde_json::from_str::<EmptyBoundedVec::<u8, 3>>("[]").is_ok());
}
}
#[cfg(feature = "arbitrary")]
#[cfg(test)]
#[allow(clippy::len_zero)]
mod arb_tests {
use super::*;
use alloc::format;
use proptest::prelude::*;
proptest! {
#[test]
fn bounded_vec_length_bounded(v: BoundedVec<u8, 1, 2>) {
prop_assert!(1 <= v.len() && v.len() <= 2);
}
}
}