use core::fmt::{self, Formatter};
use alloc::{vec, vec::Vec};
use derive_more::{AsMut, AsRef, Debug, Deref, DerefMut, Display, Error, Into, IntoIterator};
use gprimitives::utils::ByteSliceFormatter;
use scale_decode::DecodeAsType;
use scale_encode::EncodeAsType;
use scale_info::{
TypeInfo,
scale::{Decode, Encode},
};
#[derive(
Clone,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Decode,
DecodeAsType,
Encode,
EncodeAsType,
Hash,
TypeInfo,
AsRef,
AsMut,
Deref,
DerefMut,
IntoIterator,
Into,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[as_ref(forward)]
#[as_mut(forward)]
#[deref(forward)]
#[deref_mut(forward)]
#[into_iterator(owned, ref, ref_mut)]
pub struct LimitedVec<T, const N: usize>(Vec<T>);
impl<T: Clone, const N: usize> TryFrom<&[T]> for LimitedVec<T, N> {
type Error = LimitedVecError;
fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
Self::validate_len(slice.len()).map(|_| Self(slice.to_vec()))
}
}
impl<T, const N: usize> TryFrom<Vec<T>> for LimitedVec<T, N> {
type Error = LimitedVecError;
fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {
Self::validate_len(vec.len()).map(|_| Self(vec))
}
}
impl<T, const N: usize> LimitedVec<T, N> {
pub const MAX_LEN: usize = N;
const fn validate_len(len: usize) -> Result<(), LimitedVecError> {
if len <= N {
Ok(())
} else {
Err(LimitedVecError)
}
}
pub const fn new() -> Self {
Self(vec![])
}
pub fn repeat(value: T) -> Self
where
T: Clone,
{
Self(vec![value; N])
}
pub fn try_repeat(value: T, len: usize) -> Result<Self, LimitedVecError>
where
T: Clone,
{
Self::validate_len(len).map(|_| Self(vec![value; len]))
}
pub fn extend_with(&mut self, value: T)
where
T: Clone,
{
self.0.resize(N, value)
}
pub fn try_push(&mut self, value: T) -> Result<(), LimitedVecError> {
Self::validate_len(self.len() + 1)?;
self.0.push(value);
Ok(())
}
pub fn try_extend_from_slice(&mut self, values: &[T]) -> Result<(), LimitedVecError>
where
T: Clone,
{
let new_len = self
.len()
.checked_add(values.len())
.ok_or(LimitedVecError)?;
Self::validate_len(new_len)?;
self.0.extend_from_slice(values);
Ok(())
}
pub fn as_slice(&self) -> &[T] {
self
}
pub fn to_vec(&self) -> Vec<T>
where
T: Clone,
{
self.0.clone()
}
pub fn into_vec(self) -> Vec<T> {
self.0
}
}
impl<T, const N: usize> fmt::Display for LimitedVec<T, N>
where
[T]: AsRef<[u8]>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let bytes = ByteSliceFormatter::Dynamic(self.0.as_slice().as_ref());
let fmt_bytes = |f: &mut Formatter, bytes| {
if f.alternate() {
write!(f, "LimitedVec({bytes})")
} else {
write!(f, "{bytes}")
}
};
if let Some(precision) = f.precision() {
fmt_bytes(f, format_args!("{bytes:.precision$}"))
} else if f.sign_plus() {
fmt_bytes(f, format_args!("{bytes}"))
} else {
fmt_bytes(f, format_args!("{bytes:.8}"))
}
}
}
impl<T, const N: usize> fmt::Debug for LimitedVec<T, N>
where
[T]: AsRef<[u8]>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Display, Error)]
#[display("{}", Self::MESSAGE)]
pub struct LimitedVecError;
impl LimitedVecError {
pub const MESSAGE: &str = "vector length limit is exceeded";
pub const fn as_str(&self) -> &'static str {
Self::MESSAGE
}
}
#[cfg(test)]
mod test {
use super::LimitedVec;
use alloc::{string::String, vec, vec::Vec};
use core::convert::TryFrom;
const N: usize = 1000;
type TestBuffer = LimitedVec<u8, N>;
const M: usize = 64;
type SmallTestBuffer = LimitedVec<u8, M>;
#[test]
fn test_try_from() {
let v1 = vec![1; N];
let v2 = vec![1; N + 1];
let v3 = vec![1; N - 1];
let x = TestBuffer::try_from(v1).unwrap();
let _ = TestBuffer::try_from(v2).expect_err("Must be err because of size overflow");
let z = TestBuffer::try_from(v3).unwrap();
assert_eq!(x.len(), N);
assert_eq!(z.len(), N - 1);
assert_eq!(x[N / 2], 1);
assert_eq!(z[N / 2], 1);
}
#[test]
fn test_repeat() {
let x = LimitedVec::<u32, N>::repeat(0);
assert_eq!(x.len(), N);
let y = LimitedVec::<i32, 3>::repeat(-4);
assert_eq!(y.as_slice(), &[-4, -4, -4]);
}
#[test]
fn test_try_repeat() {
let x = LimitedVec::<String, N>::try_repeat(String::new(), N).unwrap();
assert!(
LimitedVec::<u64, N>::try_repeat(0, N + 1).is_err(),
"Must be error because of size overflow"
);
let y = LimitedVec::<char, 7>::try_repeat('@', 5).unwrap();
let z = LimitedVec::<Vec<u8>, N>::try_repeat(vec![], 0).unwrap();
assert_eq!(x.len(), N);
assert_eq!(z.len(), 0);
assert_eq!(x[N / 2], "");
assert_eq!(y.as_slice(), &['@', '@', '@', '@', '@']);
}
#[test]
fn test_full() {
let mut x = TestBuffer::try_from(vec![1; N]).unwrap();
let mut y = TestBuffer::try_from(vec![2; N / 2]).unwrap();
let mut z = TestBuffer::try_from(vec![3; 0]).unwrap();
x.try_extend_from_slice(&[1, 2, 3]).unwrap_err();
y.try_extend_from_slice(&[1, 2, 3]).unwrap();
z.try_extend_from_slice(&[1, 2, 3]).unwrap();
x.try_push(42).unwrap_err();
y.try_push(42).unwrap();
z.try_push(42).unwrap();
x.try_extend_from_slice(&[1, 2, 3]).unwrap_err();
y.try_extend_from_slice(&[1, 2, 3]).unwrap();
z.try_extend_from_slice(&[1, 2, 3]).unwrap();
z[0] = 0;
assert_eq!(&z.into_vec(), &[0, 2, 3, 42, 1, 2, 3]);
assert_eq!(TestBuffer::MAX_LEN, N);
}
#[test]
fn formatting_test() {
use alloc::format;
let buffer = SmallTestBuffer::try_from(b"abcdefghijklmnopqrstuvwxyz012345".to_vec())
.expect("String is 64 bytes");
assert_eq!(
format!("{buffer:+?}"),
"0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435"
);
assert_eq!(
format!("{buffer:?}"),
"0x6162636465666768..797a303132333435"
);
assert_eq!(format!("{buffer:.0?}"), "0x..");
assert_eq!(format!("{buffer:.1?}"), "0x61..35");
assert_eq!(format!("{buffer:.2?}"), "0x6162..3435");
assert_eq!(format!("{buffer:.4?}"), "0x61626364..32333435");
assert_eq!(
format!("{buffer:.15?}"),
"0x6162636465666768696a6b6c6d6e6f..72737475767778797a303132333435"
);
assert_eq!(
format!("{buffer:.30?}"),
"0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435"
);
assert_eq!(
format!("{buffer:#}"),
"LimitedVec(0x6162636465666768..797a303132333435)"
);
assert_eq!(
format!("{buffer:+#}"),
"LimitedVec(0x6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435)"
);
assert_eq!(format!("{buffer:#.2}"), "LimitedVec(0x6162..3435)");
}
}