use std::{marker::PhantomData, ptr::NonNull, vec};
use serde::Deserialize;
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum Message {
String(String),
List(Vec<String>),
}
impl Message {
#[must_use]
pub const fn is_string(&self) -> bool {
matches!(self, Self::String(_))
}
#[must_use]
pub const fn is_list(&self) -> bool {
!self.is_string()
}
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn string(self) -> Option<String> {
match self {
Self::String(x) => Some(x),
#[allow(unused_variables)]
Self::List(x) => None,
}
}
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn list(self) -> Option<Vec<String>> {
match self {
#[allow(unused_variables)]
Self::String(x) => None,
Self::List(x) => Some(x),
}
}
#[must_use]
pub const fn iter(&'_ self) -> Iter<'_> {
let (ptr, len) = match self {
Self::String(x) => (std::ptr::from_ref(x), 0),
Self::List(x) => (x.as_ptr(), x.len()),
};
unsafe {
Iter {
ptr: NonNull::new_unchecked(ptr.cast_mut()),
end: ptr.add(len),
_marker: PhantomData,
}
}
}
}
impl IntoIterator for Message {
type IntoIter = std::vec::IntoIter<Self::Item>;
type Item = String;
fn into_iter(self) -> Self::IntoIter {
match self {
Self::String(_) => vec![],
Self::List(x) => x,
}
.into_iter()
}
}
impl<'a> IntoIterator for &'a Message {
type IntoIter = Iter<'a>;
type Item = &'a String;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct Iter<'a> {
ptr: NonNull<String>,
end: *const String,
_marker: PhantomData<&'a String>,
}
impl Iter<'_> {
#[inline]
const fn post_inc_start(&mut self) -> *const String {
let old = self.ptr.as_ptr();
self.ptr = unsafe { NonNull::new_unchecked(self.ptr.as_ptr().add(1)) };
old
}
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a String;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if self.ptr.as_ptr().cast_const() == self.end {
None
} else {
Some(&*self.post_inc_start())
}
}
}
}