capped string type with Deserialize impl
parent
c5efe98a6c
commit
03d5baced8
@ -0,0 +1,102 @@
|
|||||||
|
//! Module for the [`CappedString`](CappedString) type, which is a string type which always stores
|
||||||
|
//! its data inline.
|
||||||
|
|
||||||
|
use core::{str, convert::TryFrom, ops::Deref, borrow::Borrow, fmt};
|
||||||
|
|
||||||
|
/// TODO: documentation
|
||||||
|
pub struct CappedString<const N: usize> {
|
||||||
|
/// The string data. It is an invariant that this must always be valid UTF-8.
|
||||||
|
buf: [u8; N],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> CappedString<N> {
|
||||||
|
/// TODO: documentation
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(s: &str) -> Option<Self> {
|
||||||
|
unsafe { Self::from_utf8_unchecked(s.as_bytes()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: documentation
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn from_utf8_unchecked(bs: &[u8]) -> Option<Self> {
|
||||||
|
let buf = <[u8; N]>::try_from(bs).ok()?;
|
||||||
|
Some(Self { buf })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: documentation
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
unsafe { str::from_utf8_unchecked(&self.buf) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: documentation
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_uppercase<const M: usize>(&self) -> Option<CappedString<M>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Deref for CappedString<N> {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> AsRef<str> for CappedString<N> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Borrow<str> for CappedString<N> {
|
||||||
|
#[inline]
|
||||||
|
fn borrow(&self) -> &str {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de, const N: usize> serde::Deserialize<'de> for CappedString<N> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>
|
||||||
|
{
|
||||||
|
deserializer.deserialize_str(CappedStringVisitor::<N>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CappedStringVisitor<const N: usize>;
|
||||||
|
|
||||||
|
impl<'de, const N: usize> serde::de::Visitor<'de> for CappedStringVisitor<N> {
|
||||||
|
type Value = CappedString<N>;
|
||||||
|
|
||||||
|
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "a string up to {} bytes long", N)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
CappedString::new(v)
|
||||||
|
.ok_or_else(|| E::invalid_length(v.len(), &self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
str::from_utf8(v)
|
||||||
|
.map_err(|_| E::invalid_value(serde::de::Unexpected::Bytes(v), &self))
|
||||||
|
.and_then(|v| CappedString::new(v)
|
||||||
|
.ok_or_else(|| E::invalid_length(v.len(), &self)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
//! Utilities for use by code generated by `enumscribe_derive`.
|
||||||
|
|
||||||
|
pub mod capped_string;
|
||||||
Loading…
Reference in New Issue