use core::fmt;
use core::marker::PhantomData;
use crate::error::Error;
use crate::utils::init;
use super::{EitherIter, FromTLV, TLVElement, TLVSequenceIter, TLVTag, TLVWrite, ToTLV, TLV};
pub type AnyContainer = ();
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ArrayContainer;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ListContainer;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StructContainer;
pub type TLVArray<'a, T> = TLVContainer<'a, T, ArrayContainer>;
pub type TLVList<'a, T> = TLVContainer<'a, T, ListContainer>;
pub type TLVStruct<'a, T> = TLVContainer<'a, T, StructContainer>;
#[derive(Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct TLVContainer<'a, T, C = AnyContainer> {
element: TLVElement<'a>,
_type: PhantomData<fn() -> T>,
_container_type: PhantomData<C>,
}
impl<'a, T, C> TLVContainer<'a, T, C>
where
T: FromTLV<'a>,
{
pub const fn new_unchecked(element: TLVElement<'a>) -> Self {
Self {
element,
_type: PhantomData,
_container_type: PhantomData,
}
}
pub fn element(&self) -> &TLVElement<'a> {
&self.element
}
pub fn iter(&self) -> TLVContainerIter<'a, T> {
TLVContainerIter::new(unwrap!(self.element.container()).iter())
}
}
impl<'a, T> TLVContainer<'a, T, AnyContainer>
where
T: FromTLV<'a>,
{
pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
if !element.is_empty() {
element.container()?;
}
Ok(Self::new_unchecked(element))
}
}
impl<'a, T> TLVContainer<'a, T, ArrayContainer>
where
T: FromTLV<'a>,
{
pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
if !element.is_empty() {
element.array()?;
}
Ok(Self::new_unchecked(element))
}
}
impl<'a, T> TLVContainer<'a, T, ListContainer>
where
T: FromTLV<'a>,
{
pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
if !element.is_empty() {
element.list()?;
}
Ok(Self::new_unchecked(element))
}
}
impl<'a, T> TLVContainer<'a, T, StructContainer>
where
T: FromTLV<'a>,
{
pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
if !element.is_empty() {
element.structure()?;
}
Ok(Self::new_unchecked(element))
}
}
impl<'a, T, C> IntoIterator for TLVContainer<'a, T, C>
where
T: FromTLV<'a>,
{
type Item = Result<T, Error>;
type IntoIter = TLVContainerIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T, C> IntoIterator for &TLVContainer<'a, T, C>
where
T: FromTLV<'a>,
{
type Item = Result<T, Error>;
type IntoIter = TLVContainerIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T, C> fmt::Debug for TLVContainer<'a, T, C>
where
T: FromTLV<'a> + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
let mut first = true;
for elem in self.iter() {
if first {
first = false;
write!(f, "{elem:?}")?;
} else {
write!(f, ", {elem:?}")?;
}
}
write!(f, "]")
}
}
#[cfg(feature = "defmt")]
impl<'a, T, C> defmt::Format for TLVContainer<'a, T, C>
where
T: FromTLV<'a> + defmt::Format,
{
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "[");
let mut first = true;
for elem in self.iter() {
if first {
first = false;
defmt::write!(f, "{:?}", elem);
} else {
defmt::write!(f, ", {:?}", elem);
}
}
defmt::write!(f, "]")
}
}
impl<'a, T, C> FromTLV<'a> for TLVContainer<'a, T, C>
where
T: FromTLV<'a>,
C: 'a,
{
fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
Ok(Self::new_unchecked(element.clone()))
}
}
impl<T, C> ToTLV for TLVContainer<'_, T, C> {
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
self.element.to_tlv(tag, tw)
}
fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
self.element.tlv_iter(tag)
}
}
#[repr(transparent)]
pub struct TLVContainerIter<'a, T> {
iter: TLVSequenceIter<'a>,
_type: PhantomData<fn() -> T>,
}
impl<'a, T> TLVContainerIter<'a, T>
where
T: FromTLV<'a>,
{
pub const fn new(iter: TLVSequenceIter<'a>) -> Self {
Self {
iter,
_type: PhantomData,
}
}
pub fn try_next(&mut self) -> Option<Result<T, Error>> {
let tlv = self.iter.next()?;
Some(tlv.and_then(|tlv| T::from_tlv(&tlv)))
}
pub fn try_next_init(&mut self) -> Option<Result<impl init::Init<T, Error> + 'a, Error>> {
let tlv = self.iter.next()?;
Some(tlv.map(|tlv| T::init_from_tlv(tlv)))
}
}
impl<'a, T> Iterator for TLVContainerIter<'a, T>
where
T: FromTLV<'a>,
{
type Item = Result<T, Error>;
fn next(&mut self) -> Option<Self::Item> {
self.try_next()
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TLVArrayOrSlice<'a, T>
where
T: FromTLV<'a>,
{
Array(TLVArray<'a, T>),
Slice(&'a [T]),
}
impl<'a, T> TLVArrayOrSlice<'a, T>
where
T: FromTLV<'a>,
{
pub const fn new_array(array: TLVArray<'a, T>) -> Self {
Self::Array(array)
}
pub const fn new_slice(slice: &'a [T]) -> Self {
Self::Slice(slice)
}
pub fn iter(&self) -> Result<TLVArrayOrSliceIter<'a, T>, Error> {
match self {
Self::Array(array) => Ok(TLVArrayOrSliceIter::Array(array.iter())),
Self::Slice(slice) => Ok(TLVArrayOrSliceIter::Slice(slice.iter())),
}
}
}
impl<'a, T> FromTLV<'a> for TLVArrayOrSlice<'a, T>
where
T: FromTLV<'a>,
{
fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
Ok(Self::new_array(TLVArray::new(element.clone())?))
}
}
impl<'a, T> ToTLV for TLVArrayOrSlice<'a, T>
where
T: FromTLV<'a>,
T: ToTLV,
{
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
match self {
Self::Array(array) => array.to_tlv(tag, tw),
Self::Slice(slice) => slice.to_tlv(tag, tw),
}
}
fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
match self {
Self::Array(array) => EitherIter::First(array.tlv_iter(tag)),
Self::Slice(slice) => EitherIter::Second(slice.tlv_iter(tag)),
}
}
}
pub enum TLVArrayOrSliceIter<'a, T> {
Array(TLVContainerIter<'a, T>),
Slice(core::slice::Iter<'a, T>),
}
impl<'a, T> Iterator for TLVArrayOrSliceIter<'a, T>
where
T: FromTLV<'a> + Clone,
{
type Item = Result<T, Error>;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Array(array) => array.next(),
Self::Slice(slice) => slice.next().cloned().map(|t| Ok(t)),
}
}
}