jam-std-common 0.1.26

Common datatypes and utilities for the JAM nodes and tooling
Documentation
use bounded_collections::{BoundedVec, ConstU32};
use codec::{Decode, Encode, MaxEncodedLen};

/// A UTF-8 encoded string not exceeding `N` bytes in length.
#[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 {
	/// Convert `self` into a `BoundedString`, truncating if necessary. In the case of truncation,
	/// the discarded characters are replaced by "...".
	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 {
	/// Convert `self` to a `BoundedString`, truncating if necessary. In the case of truncation,
	/// the discarded characters are replaced by "...".
	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()));
	}
}