radius 0.4.0

An async/await native implementation of the RADIUS server and client for Rust.
Documentation
use crate::core::avp::{AVPType, AVP};

#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Attributes(pub(crate) Vec<AVP>);

impl Attributes {
    pub(crate) fn decode(bs: &[u8]) -> Result<Attributes, String> {
        let mut i = 0;
        let mut attrs = Vec::new();

        while bs.len() > i {
            if bs[i..].len() < 2 {
                return Err("short buffer".to_owned());
            }

            let length = bs[i + 1] as usize;
            if length > bs[i..].len() || !(2..=255).contains(&length) {
                return Err("invalid attribute length".to_owned());
            }

            attrs.push(AVP {
                typ: bs[i],
                value: if length > 2 {
                    bs[i + 2..i + length].to_vec()
                } else {
                    vec![]
                },
            });

            i += length;
        }

        Ok(Attributes(attrs))
    }

    pub(crate) fn add(&mut self, avp: AVP) {
        self.0.push(avp)
    }

    pub(crate) fn extend(&mut self, avps: Vec<AVP>) {
        self.0.extend(avps)
    }

    pub(crate) fn del(&mut self, typ: AVPType) {
        self.0.retain(|avp| avp.typ != typ);
    }

    pub(crate) fn lookup(&self, typ: AVPType) -> Option<&AVP> {
        self.0.iter().find(|avp| avp.typ == typ)
    }

    pub(crate) fn lookup_all(&self, typ: AVPType) -> Vec<&AVP> {
        self.0.iter().filter(|&avp| avp.typ == typ).collect()
    }

    pub(crate) fn encode(&self) -> Result<Vec<u8>, String> {
        let mut encoded: Vec<u8> = Vec::new();

        for avp in &self.0 {
            let attr_len = avp.value.len();
            if attr_len > 253 {
                return Err("attribute is too large".to_owned());
            }
            encoded.push(avp.typ);
            encoded.push(2 + attr_len as u8);
            encoded.extend(&avp.value);
        }

        Ok(encoded)
    }
}