use std::convert::TryFrom;
use std::mem::size_of;
use std::os::raw::c_int;
use std::slice;
use crate::ffi::nlattr;
use crate::sys::{align, Bytes};
use super::{Buffer, Error};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Attr<'a> {
kind: u16,
value: Value<'a>
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Value<'a> {
AsciiZ(&'a str),
Bytes(&'a [u8]),
Number(Number),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Number {
U8 (u8 ),
U16(u16),
U32(u32),
CInt(c_int),
}
impl<'a> Attr<'a> {
pub fn write(&self, buf: &mut Buffer<'_>) -> Result<(), Error> {
let n = self.value.size();
buf.write(&nlattr {
nla_len: nla_len(n)?,
nla_type: self.kind,
})?;
let buffer = buf.buffer(n)?;
let buffer = &mut buffer[..n];
self.value.write(buffer)?;
Ok(())
}
}
impl<'a> Value<'a> {
fn write(&self, buf: &mut [u8]) -> Result<(), Error> {
match self {
Self::AsciiZ(v) => v.write(buf),
Self::Bytes(v) => v.write(buf),
Self::Number(v) => v.write(buf),
}
Ok(())
}
fn size(&self) -> usize {
match self {
Self::AsciiZ(v) => v.len() + 1,
Self::Bytes(v) => v.len(),
Self::Number(v) => v.size(),
}
}
}
impl Number {
fn size(self) -> usize {
match self {
Self::U8(_) => size_of::<u8>(),
Self::U16(_) => size_of::<u16>(),
Self::U32(_) => size_of::<u32>(),
Self::CInt(_) => size_of::<c_int>(),
}
}
}
pub fn attr<'a, T: Into<Value<'a>>>(kind: u16, value: T) -> Attr<'a> {
Attr::from((kind, value.into()))
}
impl<'a> From<(u16, Value<'a>)> for Attr<'a> {
fn from((kind, value): (u16, Value<'a>)) -> Self {
Self {
kind: kind,
value: value,
}
}
}
impl<'a> From<&'a str> for Value<'a> {
fn from(v: &'a str) -> Self {
Self::AsciiZ(v)
}
}
impl<'a> From<&'a [u8]> for Value<'a> {
fn from(v: &'a [u8]) -> Self {
Self::Bytes(v)
}
}
impl<'a, T: Bytes> From<&'a T> for Value<'a> {
fn from(v: &'a T) -> Self {
Self::Bytes(unsafe {
let ptr = v as *const T as *const u8;
let len = size_of::<T>();
slice::from_raw_parts(ptr, len)
})
}
}
impl<'a> From<u8> for Value<'a> {
fn from(v: u8) -> Self {
Self::Number(Number::U8(v))
}
}
impl<'a> From<u16> for Value<'a> {
fn from(v: u16) -> Self {
Self::Number(Number::U16(v))
}
}
impl<'a> From<u32> for Value<'a> {
fn from(v: u32) -> Self {
Self::Number(Number::U32(v))
}
}
impl<'a> From<c_int> for Value<'a> {
fn from(v: c_int) -> Self {
Self::Number(Number::CInt(v))
}
}
trait Write {
fn write(&self, buf: &mut [u8]);
}
impl Write for Number {
fn write(&self, buf: &mut [u8]) {
match self {
Self::U8 (n) => buf.copy_from_slice(&[*n]),
Self::U16(n) => buf.copy_from_slice(&n.to_le_bytes()),
Self::U32(n) => buf.copy_from_slice(&n.to_le_bytes()),
Self::CInt(n) => buf.copy_from_slice(&n.to_le_bytes()),
}
}
}
impl Write for &str {
fn write(&self, buf: &mut [u8]) {
let bytes = self.as_bytes();
let n = bytes.len();
let (s, z) = buf.split_at_mut(n);
s.copy_from_slice(bytes);
z.copy_from_slice(&[0]);
}
}
impl Write for &[u8] {
fn write(&self, buf: &mut [u8]) {
buf.copy_from_slice(self);
}
}
fn nla_len(size: usize) -> Result<u16, Error> {
let head = align(size_of::<nlattr>());
let size = head.saturating_add(size);
Ok(u16::try_from(size)?)
}