use core::fmt::Debug;
use core::iter::empty;
use crate::error::Error;
use crate::utils::init;
use crate::utils::maybe::Maybe;
use super::{EitherIter, FromTLV, TLVElement, TLVTag, TLVValueType, TLVWrite, ToTLV, TLV};
pub type AsOptional = ();
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AsNullable;
pub type Optional<T> = Maybe<T, AsOptional>;
pub type Nullable<T> = Maybe<T, AsNullable>;
impl<'a, T: FromTLV<'a>> FromTLV<'a> for Maybe<T, AsNullable> {
fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
match element.control()?.value_type {
TLVValueType::Null => Ok(Maybe::none()),
_ => T::nullable_from_tlv(element).map(Maybe::some),
}
}
fn init_from_tlv(element: TLVElement<'a>) -> impl init::Init<Self, Error> {
unsafe {
init::init_from_closure(move |slot| {
let init = match element.control()?.value_type {
TLVValueType::Null => None,
_ => Some(T::init_nullable_from_tlv(element)),
};
init::Init::__init(Maybe::init(init), slot)
})
}
}
}
impl<T: ToTLV> ToTLV for Maybe<T, AsNullable> {
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
match self.as_opt_ref() {
None => tw.null(tag),
Some(s) => s.nullable_to_tlv(tag, tw),
}
}
fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
match self.as_opt_ref() {
None => EitherIter::First(TLV::null(tag).into_tlv_iter()),
Some(s) => EitherIter::Second(s.nullable_tlv_iter(tag)),
}
}
}
impl<'a, T: FromTLV<'a> + 'a> FromTLV<'a> for Maybe<T, AsOptional> {
fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
if element.is_empty() {
Ok(Self::none())
} else {
Ok(Self::some(T::from_tlv(element)?))
}
}
fn init_from_tlv(element: TLVElement<'a>) -> impl init::Init<Self, Error> {
if element.is_empty() {
Self::init(None)
} else {
Self::init(Some(T::init_from_tlv(element)))
}
}
}
impl<T: ToTLV> ToTLV for Maybe<T, AsOptional> {
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
match self.as_opt_ref() {
None => Ok(()),
Some(s) => s.to_tlv(tag, tw),
}
}
fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
use crate::tlv::EitherIter;
match self.as_opt_ref() {
None => EitherIter::First(empty()),
Some(s) => EitherIter::Second(s.tlv_iter(tag)),
}
}
}
impl<'a, T: FromTLV<'a>> FromTLV<'a> for Option<T> {
fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
if element.is_empty() {
return Ok(None);
}
Ok(Some(T::from_tlv(element)?))
}
}
impl<T: ToTLV> ToTLV for Option<T> {
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
match self.as_ref() {
None => Ok(()),
Some(s) => s.to_tlv(tag, tw),
}
}
fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
match self.as_ref() {
None => EitherIter::First(empty()),
Some(s) => EitherIter::Second(s.tlv_iter(tag)),
}
}
}