use std::{fmt::Debug, marker::PhantomData};
use crate::{
consts::nl::{NlType, NlmF, Nlmsg},
err::NlError,
nl::{NlPayload, Nlmsghdr},
socket::NlSocketHandle,
FromBytesWithInput,
};
#[derive(PartialEq, Eq)]
pub enum IterationBehavior {
EndMultiOnDone,
IterIndefinitely,
}
pub struct NlMessageIter<'a, T, P> {
sock_ref: &'a mut NlSocketHandle,
next_is_none: Option<bool>,
type_: PhantomData<T>,
payload: PhantomData<P>,
}
impl<'a, T, P> NlMessageIter<'a, T, P>
where
T: NlType + Debug,
P: FromBytesWithInput<'a, Input = usize> + Debug,
{
pub fn new(sock_ref: &'a mut NlSocketHandle, behavior: IterationBehavior) -> Self {
NlMessageIter {
sock_ref,
next_is_none: if behavior == IterationBehavior::IterIndefinitely {
None
} else {
Some(false)
},
type_: PhantomData,
payload: PhantomData,
}
}
fn next<TT, PP>(&mut self) -> Option<Result<Nlmsghdr<TT, PP>, NlError<TT, PP>>>
where
TT: NlType + Debug,
PP: for<'c> FromBytesWithInput<'c, Input = usize> + Debug,
{
if let Some(true) = self.next_is_none {
return None;
}
let next_res = self.sock_ref.recv::<TT, PP>();
let next = match next_res {
Ok(Some(n)) => n,
Ok(None) => return None,
Err(e) => return Some(Err(e)),
};
if let NlPayload::Ack(_) = next.nl_payload {
self.next_is_none = self.next_is_none.map(|_| true);
} else if (!next.nl_flags.contains(&NlmF::Multi)
|| next.nl_type.into() == Nlmsg::Done.into())
&& !self.sock_ref.needs_ack
{
self.next_is_none = self.next_is_none.map(|_| true);
}
Some(Ok(next))
}
}
impl<T, P> Iterator for NlMessageIter<'_, T, P>
where
T: NlType + Debug,
P: for<'b> FromBytesWithInput<'b, Input = usize> + Debug,
{
type Item = Result<Nlmsghdr<T, P>, NlError<T, P>>;
fn next(&mut self) -> Option<Self::Item> {
NlMessageIter::next::<T, P>(self)
}
}