use std::convert::TryFrom;
use std::mem::size_of;
use std::num::TryFromIntError;
use crate::ffi::nlmsghdr;
use crate::sys::{align, Bytes, Cursor, Message};
use super::{Attr, Buffer, Error};
pub struct Builder<'a, T: Bytes> {
head: nlmsghdr,
data: T,
tail: &'a [Attr<'a>],
}
pub fn message<'a, T: Bytes>(kind: u16, data: T, tail: &'a [Attr<'a>]) -> Builder<'a, T> {
let mut head = nlmsghdr::default();
head.nlmsg_type = kind;
Builder { head, data, tail }
}
impl<'a, T: Bytes> Builder<'a, T> {
pub fn flags(mut self, flags: u16) -> Self {
self.head.nlmsg_flags = flags;
self
}
pub fn build(self, buf: &mut [u8]) -> Result<Message<'_, T>, Error> {
let Self { head, data, tail } = self;
let mut buf = Buffer::new(buf);
for item in tail {
item.write(&mut buf)?;
}
let tail = Cursor::from(buf.bytes());
Ok(Message::with(head, data, tail))
}
}
pub fn write<'a, T: Bytes>(buf: &'a mut [u8], msg: &Message<T>) -> Result<&'a [u8], Error> {
let mut w = Buffer::new(buf);
let (head, data, tail) = msg.parts();
let tail = tail.bytes();
let head = &nlmsghdr {
nlmsg_len: nlmsg_len::<T>(tail)?,
nlmsg_type: head.nlmsg_type,
nlmsg_flags: head.nlmsg_flags,
nlmsg_seq: head.nlmsg_seq,
nlmsg_pid: head.nlmsg_pid,
};
w.write(head)?;
w.write(data)?;
w.copy(&tail)?;
Ok(w.bytes())
}
pub fn nlmsg_len<T>(tail: &[u8]) -> Result<u32, TryFromIntError> {
let head = align(size_of::<nlmsghdr>());
let data = align(size_of::<T>());
let tail = align(tail.len());
u32::try_from(head.saturating_add(data).saturating_add(tail))
}