nell 0.3.0

Linux netlink interface
Documentation
// Copyright (C) 2020 - Will Glozer. All rights reserved.

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))
}