use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use crate::{Decode, Encode};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct List<T, S>
where
S: Separator,
{
vec: Vec<T>,
_marker: PhantomData<fn() -> S>,
}
impl<T, S> List<T, S>
where
S: Separator,
{
#[inline]
pub const fn new(vec: Vec<T>) -> Self {
Self {
vec,
_marker: PhantomData,
}
}
#[inline]
pub fn into_inner(self) -> Vec<T> {
self.vec
}
}
impl<T, S> Default for List<T, S>
where
S: Separator,
{
#[inline]
fn default() -> Self {
Self::new(Vec::new())
}
}
impl<T, S> Deref for List<T, S>
where
S: Separator,
{
type Target = Vec<T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.vec
}
}
impl<T, S> DerefMut for List<T, S>
where
S: Separator,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.vec
}
}
impl<T, S> From<Vec<T>> for List<T, S>
where
S: Separator,
{
#[inline]
fn from(value: Vec<T>) -> Self {
Self::new(value)
}
}
impl<T, S> From<List<T, S>> for Vec<T>
where
S: Separator,
{
#[inline]
fn from(value: List<T, S>) -> Self {
value.into_inner()
}
}
impl<T, S> Encode for List<T, S>
where
T: Encode,
S: Separator,
{
fn encode(&self, buf: &mut String) {
if let Some(elem) = self.vec.get(0) {
elem.encode(buf);
}
for elem in self.vec.iter().skip(1) {
buf.push_str(S::PATTERN);
elem.encode(buf);
}
}
}
impl<T, S> Decode for List<T, S>
where
T: Decode,
S: Separator,
{
type Error = <T as Decode>::Error;
fn decode(buf: &[u8]) -> Result<Self, Self::Error> {
let mut vec = Vec::new();
for b in bytes_split(buf, S::PATTERN.as_bytes()) {
vec.push(T::decode(b)?);
}
Ok(Self {
vec,
_marker: PhantomData,
})
}
}
pub trait Separator {
const PATTERN: &'static str;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Pipe;
impl Separator for Pipe {
const PATTERN: &'static str = "|";
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Comma;
impl Separator for Comma {
const PATTERN: &'static str = ",";
}
fn bytes_split<'a>(mut buf: &'a [u8], pat: &[u8]) -> Vec<&'a [u8]> {
let mut cursor = 0;
let mut segs = Vec::new();
while buf.len() - cursor >= pat.len() {
let slice = &buf[cursor..cursor + pat.len()];
if slice == pat {
segs.push(&buf[0..cursor]);
if buf.len() <= pat.len() {
return segs;
}
buf = &buf[cursor + pat.len()..];
cursor = 0;
} else {
cursor += 1;
}
}
segs.push(buf);
segs
}
#[cfg(test)]
mod tests {
use super::{List, Pipe};
use crate::shared::list::bytes_split;
use crate::Decode;
#[test]
fn test_bytes_split() {
assert_eq!(bytes_split(b"a|b|c", b"|"), [b"a", b"b", b"c"]);
assert_eq!(bytes_split(b"abc", b"|"), [b"abc"]);
assert_eq!(
bytes_split(b"a|bc", b"|"),
[b"a".as_slice(), b"bc".as_slice()]
);
assert_eq!(
bytes_split(b"a|bc|", b"|"),
[b"a".as_slice(), b"bc".as_slice(), b"".as_slice()]
);
assert_eq!(bytes_split(b"ABCabcABC", b"abc"), [b"ABC", b"ABC"]);
assert_eq!(
bytes_split(b"00abcd0e0f00g000", b"0"),
[
b"".as_slice(),
b"".as_slice(),
b"abcd".as_slice(),
b"e".as_slice(),
b"f".as_slice(),
b"".as_slice(),
b"g".as_slice(),
b"".as_slice(),
b"".as_slice()
]
);
}
#[test]
fn test_list_decode() {
let input = b"test|test2";
assert_eq!(
&*List::<String, Pipe>::decode(input).unwrap(),
&["test", "test2"]
);
}
}