use bounded_collections::{BoundedVec, ConstU32};
use codec::{Decode, Encode, MaxEncodedLen};
#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Encode)]
pub struct BoundedString<const N: u32>(String);
impl<const N: u32> std::fmt::Debug for BoundedString<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl<const N: u32> std::fmt::Display for BoundedString<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl<const N: u32> std::ops::Deref for BoundedString<N> {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const N: u32> Decode for BoundedString<N> {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
let v = BoundedVec::<u8, ConstU32<N>>::decode(input)?;
String::from_utf8(v.into_inner()).map_err(|_| "Invalid UTF-8".into()).map(Self)
}
}
impl<const N: u32> MaxEncodedLen for BoundedString<N> {
fn max_encoded_len() -> usize {
BoundedVec::<u8, ConstU32<N>>::max_encoded_len()
}
}
pub trait IntoTruncated {
fn into_truncated<const N: u32>(self) -> BoundedString<N>;
}
impl IntoTruncated for String {
fn into_truncated<const N: u32>(mut self) -> BoundedString<N> {
if self.len() > (N as usize) {
while self.len() > (N.saturating_sub(3) as usize) {
self.pop();
}
while self.len() < (N as usize) {
self.push('.');
}
debug_assert!(self.len() <= (N as usize));
}
BoundedString(self)
}
}
pub trait Truncated {
fn truncated<const N: u32>(&self) -> BoundedString<N>;
}
impl<T: ToString + ?Sized> Truncated for T {
fn truncated<const N: u32>(&self) -> BoundedString<N> {
self.to_string().into_truncated()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn truncation() {
debug_assert_eq!("abc".truncated(), BoundedString::<4>("abc".into()));
debug_assert_eq!("abc".truncated(), BoundedString::<3>("abc".into()));
debug_assert_eq!("abcd".truncated(), BoundedString::<3>("...".into()));
debug_assert_eq!("abc".truncated(), BoundedString::<2>("..".into()));
debug_assert_eq!("abc".truncated(), BoundedString::<1>(".".into()));
debug_assert_eq!("abc".truncated(), BoundedString::<0>("".into()));
debug_assert_eq!("abcdefg".truncated(), BoundedString::<6>("abc...".into()));
}
}