diameter/avp/
mod.rs

1//! # AVP Module
2//!
3//! This module defines the structure and functionalities related to AVPs in Diameter messages.
4//!
5//! ## AVP Format
6//! The diagram below illustrates the format for an AVP:
7//! ```text
8//!   0                   1                   2                   3
9//!   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
10//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11//!  |                         Command-Code                          |
12//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13//!  |  Flags       |                 AVP Length                     |
14//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15//!  |                         Vendor ID (optional)                  |
16//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17//!  |                             Data                              |
18//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19//!  |                             Data             |    Padding     |
20//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21//!
22//!  AVP Flags:
23//!    0 1 2 3 4 5 6 7
24//!   +-+-+-+-+-+-+-+-+  V(endor), M(andatory), P(rivate)
25//!   |V M P r r r r r|  r(eserved)
26//!   +-+-+-+-+-+-+-+-+
27//! ```
28//!
29
30pub mod address;
31pub mod enumerated;
32pub mod float32;
33pub mod float64;
34pub mod group;
35pub mod identity;
36pub mod integer32;
37pub mod integer64;
38pub mod ipv4;
39pub mod ipv6;
40pub mod octetstring;
41pub mod time;
42pub mod unsigned32;
43pub mod unsigned64;
44pub mod uri;
45pub mod utf8string;
46
47use crate::dictionary::Dictionary;
48use crate::error::{Error, Result};
49use core::fmt;
50use std::io::Read;
51use std::io::Seek;
52use std::io::SeekFrom;
53use std::io::Write;
54
55pub use crate::avp::address::Address;
56pub use crate::avp::enumerated::Enumerated;
57pub use crate::avp::float32::Float32;
58pub use crate::avp::float64::Float64;
59pub use crate::avp::group::Grouped;
60pub use crate::avp::identity::Identity;
61pub use crate::avp::integer32::Integer32;
62pub use crate::avp::integer64::Integer64;
63pub use crate::avp::ipv4::IPv4;
64pub use crate::avp::ipv6::IPv6;
65pub use crate::avp::octetstring::OctetString;
66pub use crate::avp::time::Time;
67pub use crate::avp::unsigned32::Unsigned32;
68pub use crate::avp::unsigned64::Unsigned64;
69pub use crate::avp::uri::DiameterURI;
70pub use crate::avp::utf8string::UTF8String;
71pub use std::sync::Arc;
72
73pub mod flags {
74    pub const V: u8 = 0x80;
75    pub const M: u8 = 0x40;
76    pub const P: u8 = 0x20;
77}
78
79#[derive(Debug, Clone)]
80pub struct Avp {
81    header: AvpHeader,
82    value: AvpValue,
83    padding: u8,
84    dict: Arc<Dictionary>,
85}
86
87#[derive(Debug, Clone)]
88pub struct AvpHeader {
89    code: u32,
90    flags: AvpFlags,
91    length: u32,
92    vendor_id: Option<u32>,
93}
94
95#[derive(Debug, Clone)]
96pub struct AvpFlags {
97    pub vendor: bool,
98    pub mandatory: bool,
99    pub private: bool,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq)]
103pub enum AvpType {
104    Unknown,
105    Address,
106    AddressIPv4,
107    AddressIPv6,
108    Identity,
109    DiameterURI,
110    Enumerated,
111    Float32,
112    Float64,
113    Grouped,
114    Integer32,
115    Integer64,
116    OctetString,
117    Time,
118    Unsigned32,
119    Unsigned64,
120    UTF8String,
121}
122
123#[derive(Debug, Clone)]
124pub enum AvpValue {
125    Address(Address),
126    AddressIPv4(IPv4),
127    AddressIPv6(IPv6),
128    Identity(Identity),
129    DiameterURI(DiameterURI),
130    Enumerated(Enumerated),
131    Float32(Float32),
132    Float64(Float64),
133    Grouped(Grouped),
134    Integer32(Integer32),
135    Integer64(Integer64),
136    OctetString(OctetString),
137    Time(Time),
138    Unsigned32(Unsigned32),
139    Unsigned64(Unsigned64),
140    UTF8String(UTF8String),
141}
142
143impl fmt::Display for AvpValue {
144    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145        self.fmt(f, 0)
146    }
147}
148
149impl AvpValue {
150    pub fn length(&self) -> u32 {
151        match self {
152            AvpValue::Address(avp) => avp.length(),
153            AvpValue::AddressIPv4(avp) => avp.length(),
154            AvpValue::AddressIPv6(avp) => avp.length(),
155            AvpValue::Float32(avp) => avp.length(),
156            AvpValue::Float64(avp) => avp.length(),
157            AvpValue::Enumerated(avp) => avp.length(),
158            AvpValue::Integer32(avp) => avp.length(),
159            AvpValue::Integer64(avp) => avp.length(),
160            AvpValue::Unsigned32(avp) => avp.length(),
161            AvpValue::Unsigned64(avp) => avp.length(),
162            AvpValue::UTF8String(avp) => avp.length(),
163            AvpValue::OctetString(avp) => avp.length(),
164            AvpValue::Identity(avp) => avp.length(),
165            AvpValue::DiameterURI(avp) => avp.length(),
166            AvpValue::Time(avp) => avp.length(),
167            AvpValue::Grouped(avp) => avp.length(),
168        }
169    }
170
171    pub fn get_type_name(&self) -> &'static str {
172        match self {
173            AvpValue::Address(_) => "Address",
174            AvpValue::AddressIPv4(_) => "AddressIPv4",
175            AvpValue::AddressIPv6(_) => "AddressIPv6",
176            AvpValue::Float32(_) => "Float32",
177            AvpValue::Float64(_) => "Float64",
178            AvpValue::Enumerated(_) => "Enumerated",
179            AvpValue::Integer32(_) => "Integer32",
180            AvpValue::Integer64(_) => "Integer64",
181            AvpValue::Unsigned32(_) => "Unsigned32",
182            AvpValue::Unsigned64(_) => "Unsigned64",
183            AvpValue::UTF8String(_) => "UTF8String",
184            AvpValue::OctetString(_) => "OctetString",
185            AvpValue::Identity(_) => "Identity",
186            AvpValue::DiameterURI(_) => "DiameterURI",
187            AvpValue::Time(_) => "Time",
188            AvpValue::Grouped(_) => "Grouped",
189        }
190    }
191
192    fn fmt(&self, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
193        match self {
194            AvpValue::Address(avp) => write!(f, "{}", avp),
195            AvpValue::AddressIPv4(avp) => write!(f, "{}", avp),
196            AvpValue::AddressIPv6(avp) => write!(f, "{}", avp),
197            AvpValue::Float32(avp) => write!(f, "{}", avp),
198            AvpValue::Float64(avp) => write!(f, "{}", avp),
199            AvpValue::Enumerated(avp) => write!(f, "{}", avp),
200            AvpValue::Integer32(avp) => write!(f, "{}", avp),
201            AvpValue::Integer64(avp) => write!(f, "{}", avp),
202            AvpValue::Unsigned32(avp) => write!(f, "{}", avp),
203            AvpValue::Unsigned64(avp) => write!(f, "{}", avp),
204            AvpValue::UTF8String(avp) => write!(f, "{}", avp),
205            AvpValue::OctetString(avp) => write!(f, "{}", avp),
206            AvpValue::Identity(avp) => write!(f, "{}", avp),
207            AvpValue::DiameterURI(avp) => write!(f, "{}", avp),
208            AvpValue::Time(avp) => write!(f, "{}", avp),
209            AvpValue::Grouped(avp) => avp.fmt(f, depth),
210        }
211    }
212}
213
214impl From<Identity> for AvpValue {
215    fn from(identity: Identity) -> Self {
216        AvpValue::Identity(identity)
217    }
218}
219
220impl From<DiameterURI> for AvpValue {
221    fn from(uri: DiameterURI) -> Self {
222        AvpValue::DiameterURI(uri)
223    }
224}
225
226impl From<Enumerated> for AvpValue {
227    fn from(enumerated: Enumerated) -> Self {
228        AvpValue::Enumerated(enumerated)
229    }
230}
231
232impl From<Float32> for AvpValue {
233    fn from(float32: Float32) -> Self {
234        AvpValue::Float32(float32)
235    }
236}
237
238impl From<Float64> for AvpValue {
239    fn from(float64: Float64) -> Self {
240        AvpValue::Float64(float64)
241    }
242}
243
244impl From<Integer32> for AvpValue {
245    fn from(integer32: Integer32) -> Self {
246        AvpValue::Integer32(integer32)
247    }
248}
249
250impl From<Integer64> for AvpValue {
251    fn from(integer64: Integer64) -> Self {
252        AvpValue::Integer64(integer64)
253    }
254}
255
256impl From<Address> for AvpValue {
257    fn from(avp: Address) -> Self {
258        AvpValue::Address(avp)
259    }
260}
261
262impl From<IPv4> for AvpValue {
263    fn from(ipv4: IPv4) -> Self {
264        AvpValue::AddressIPv4(ipv4)
265    }
266}
267
268impl From<IPv6> for AvpValue {
269    fn from(ipv6: IPv6) -> Self {
270        AvpValue::AddressIPv6(ipv6)
271    }
272}
273
274impl From<OctetString> for AvpValue {
275    fn from(octetstring: OctetString) -> Self {
276        AvpValue::OctetString(octetstring)
277    }
278}
279
280impl From<Time> for AvpValue {
281    fn from(time: Time) -> Self {
282        AvpValue::Time(time)
283    }
284}
285
286impl From<Unsigned32> for AvpValue {
287    fn from(unsigned32: Unsigned32) -> Self {
288        AvpValue::Unsigned32(unsigned32)
289    }
290}
291
292impl From<Unsigned64> for AvpValue {
293    fn from(unsigned64: Unsigned64) -> Self {
294        AvpValue::Unsigned64(unsigned64)
295    }
296}
297
298impl From<UTF8String> for AvpValue {
299    fn from(utf8string: UTF8String) -> Self {
300        AvpValue::UTF8String(utf8string)
301    }
302}
303
304impl From<Grouped> for AvpValue {
305    fn from(group: Grouped) -> Self {
306        AvpValue::Grouped(group)
307    }
308}
309
310impl AvpHeader {
311    pub fn decode_from<R: Read>(reader: &mut R) -> Result<AvpHeader> {
312        let mut b = [0; 8];
313        reader.read_exact(&mut b)?;
314
315        let code = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
316
317        let flags = AvpFlags {
318            vendor: (b[4] & flags::V) != 0,
319            mandatory: (b[4] & flags::M) != 0,
320            private: (b[4] & flags::P) != 0,
321        };
322
323        let length = u32::from_be_bytes([0, b[5], b[6], b[7]]);
324
325        let vendor_id = if flags.vendor {
326            let mut b = [0; 4];
327            reader.read_exact(&mut b)?;
328            Some(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
329        } else {
330            None
331        };
332
333        Ok(AvpHeader {
334            code,
335            flags,
336            length,
337            vendor_id,
338        })
339    }
340
341    pub fn encode_to<W: Write>(&self, writer: &mut W) -> Result<()> {
342        // Code
343        writer.write_all(&self.code.to_be_bytes())?;
344
345        // Flags
346        let mut flags: u8 = 0;
347        if self.flags.vendor {
348            flags |= flags::V;
349        }
350        if self.flags.mandatory {
351            flags |= flags::M;
352        }
353        if self.flags.private {
354            flags |= flags::P;
355        }
356        writer.write_all(&[flags])?;
357
358        // Length
359        let length_bytes = &self.length.to_be_bytes()[1..4];
360        writer.write_all(length_bytes)?;
361
362        // Vendor ID
363        if let Some(vendor_id) = self.vendor_id {
364            writer.write_all(&vendor_id.to_be_bytes())?;
365        }
366
367        Ok(())
368    }
369}
370
371impl Avp {
372    pub fn new(
373        code: u32,
374        vendor_id: Option<u32>,
375        flags: u8,
376        value: AvpValue,
377        dict: Arc<Dictionary>,
378    ) -> Avp {
379        let header_length = if vendor_id.is_some() { 12 } else { 8 };
380        let padding = Avp::pad_to_32_bits(value.length());
381        let header = AvpHeader {
382            code,
383            flags: AvpFlags {
384                vendor: if vendor_id.is_some() { true } else { false },
385                mandatory: (flags & flags::M) != 0,
386                private: (flags & flags::P) != 0,
387            },
388            length: header_length + value.length(),
389            vendor_id,
390        };
391        return Avp {
392            header,
393            value,
394            padding,
395            dict,
396        };
397    }
398
399    pub fn from_name(avp_name: &str, value: AvpValue, dict: Arc<Dictionary>) -> Result<Avp> {
400        let avp_def = dict
401            .get_avp_by_name(avp_name)
402            .ok_or(Error::UnknownAvpName(avp_name.to_string()))?;
403
404        let flags = if avp_def.m_flag { flags::M } else { 0 };
405        Ok(Avp::new(
406            avp_def.code,
407            avp_def.vendor_id,
408            flags,
409            value,
410            dict,
411        ))
412    }
413
414    pub fn get_code(&self) -> u32 {
415        self.header.code
416    }
417
418    pub fn get_flags(&self) -> &AvpFlags {
419        &self.header.flags
420    }
421
422    pub fn get_vendor_id(&self) -> Option<u32> {
423        self.header.vendor_id
424    }
425
426    pub fn get_length(&self) -> u32 {
427        self.header.length
428    }
429
430    pub fn get_padding(&self) -> u8 {
431        self.padding
432    }
433
434    pub fn get_value(&self) -> &AvpValue {
435        &self.value
436    }
437
438    pub fn decode_from<R: Read + Seek>(reader: &mut R, dict: Arc<Dictionary>) -> Result<Avp> {
439        let header = AvpHeader::decode_from(reader)?;
440
441        let header_length = if header.flags.vendor { 12 } else { 8 };
442        let value_length = header.length - header_length;
443
444        let avp_type = dict
445            .get_avp_type(header.code, header.vendor_id)
446            .unwrap_or(&AvpType::Unknown);
447
448        let value = match avp_type {
449            AvpType::Address => {
450                AvpValue::Address(Address::decode_from(reader, value_length as usize)?)
451            }
452            AvpType::AddressIPv4 => AvpValue::AddressIPv4(IPv4::decode_from(reader)?),
453            AvpType::AddressIPv6 => AvpValue::AddressIPv6(IPv6::decode_from(reader)?),
454            AvpType::Float32 => AvpValue::Float32(Float32::decode_from(reader)?),
455            AvpType::Float64 => AvpValue::Float64(Float64::decode_from(reader)?),
456            AvpType::Enumerated => AvpValue::Enumerated(Enumerated::decode_from(reader)?),
457            AvpType::Integer32 => AvpValue::Integer32(Integer32::decode_from(reader)?),
458            AvpType::Integer64 => AvpValue::Integer64(Integer64::decode_from(reader)?),
459            AvpType::Unsigned32 => AvpValue::Unsigned32(Unsigned32::decode_from(reader)?),
460            AvpType::Unsigned64 => AvpValue::Unsigned64(Unsigned64::decode_from(reader)?),
461            AvpType::UTF8String => {
462                AvpValue::UTF8String(UTF8String::decode_from(reader, value_length as usize)?)
463            }
464            AvpType::OctetString => {
465                AvpValue::OctetString(OctetString::decode_from(reader, value_length as usize)?)
466            }
467            AvpType::Identity => {
468                AvpValue::Identity(Identity::decode_from(reader, value_length as usize)?)
469            }
470            AvpType::DiameterURI => {
471                AvpValue::DiameterURI(DiameterURI::decode_from(reader, value_length as usize)?)
472            }
473            AvpType::Time => AvpValue::Time(Time::decode_from(reader)?),
474            AvpType::Grouped => AvpValue::Grouped(Grouped::decode_from(
475                reader,
476                value_length as usize,
477                Arc::clone(&dict),
478            )?),
479            AvpType::Unknown => return Err(Error::UnknownAvpCode(header.code)),
480        };
481
482        // Skip padding
483        let padding = Avp::pad_to_32_bits(value_length);
484        if padding > 0 {
485            reader.seek(SeekFrom::Current(padding as i64))?;
486        }
487
488        return Ok(Avp {
489            header,
490            value,
491            padding,
492            dict,
493        });
494    }
495
496    pub fn encode_to<W: Write>(&self, writer: &mut W) -> Result<()> {
497        self.header.encode_to(writer)?;
498
499        let _ = match &self.value {
500            AvpValue::Address(avp) => avp.encode_to(writer),
501            AvpValue::AddressIPv4(avp) => avp.encode_to(writer),
502            AvpValue::AddressIPv6(avp) => avp.encode_to(writer),
503            AvpValue::Float32(avp) => avp.encode_to(writer),
504            AvpValue::Float64(avp) => avp.encode_to(writer),
505            AvpValue::Enumerated(avp) => avp.encode_to(writer),
506            AvpValue::Integer32(avp) => avp.encode_to(writer),
507            AvpValue::Integer64(avp) => avp.encode_to(writer),
508            AvpValue::Unsigned32(avp) => avp.encode_to(writer),
509            AvpValue::Unsigned64(avp) => avp.encode_to(writer),
510            AvpValue::UTF8String(avp) => avp.encode_to(writer),
511            AvpValue::OctetString(avp) => avp.encode_to(writer),
512            AvpValue::Identity(avp) => avp.encode_to(writer),
513            AvpValue::DiameterURI(avp) => avp.encode_to(writer),
514            AvpValue::Time(avp) => avp.encode_to(writer),
515            AvpValue::Grouped(avp) => avp.encode_to(writer),
516        };
517
518        // Padding
519        for _ in 0..self.padding {
520            writer.write_all(&[0])?;
521        }
522
523        Ok(())
524    }
525
526    fn pad_to_32_bits(length: u32) -> u8 {
527        ((4 - (length & 0b11)) % 4) as u8
528    }
529
530    pub fn get_address(&self) -> Option<&Address> {
531        match &self.value {
532            AvpValue::Address(avp) => Some(avp),
533            _ => None,
534        }
535    }
536
537    pub fn get_address_ipv4(&self) -> Option<&IPv4> {
538        match &self.value {
539            AvpValue::AddressIPv4(avp) => Some(avp),
540            _ => None,
541        }
542    }
543
544    pub fn get_address_ipv6(&self) -> Option<&IPv6> {
545        match &self.value {
546            AvpValue::AddressIPv6(avp) => Some(avp),
547            _ => None,
548        }
549    }
550
551    pub fn get_identity(&self) -> Option<&Identity> {
552        match &self.value {
553            AvpValue::Identity(avp) => Some(avp),
554            _ => None,
555        }
556    }
557
558    pub fn get_diameter_uri(&self) -> Option<&DiameterURI> {
559        match &self.value {
560            AvpValue::DiameterURI(avp) => Some(avp),
561            _ => None,
562        }
563    }
564
565    pub fn get_enumerated(&self) -> Option<&Enumerated> {
566        match &self.value {
567            AvpValue::Enumerated(avp) => Some(avp),
568            _ => None,
569        }
570    }
571
572    pub fn get_integer32(&self) -> Option<i32> {
573        match &self.value {
574            AvpValue::Integer32(avp) => Some(avp.value()),
575            _ => None,
576        }
577    }
578
579    pub fn get_integer64(&self) -> Option<i64> {
580        match &self.value {
581            AvpValue::Integer64(avp) => Some(avp.value()),
582            _ => None,
583        }
584    }
585
586    pub fn get_unsigned32(&self) -> Option<u32> {
587        match &self.value {
588            AvpValue::Unsigned32(avp) => Some(avp.value()),
589            _ => None,
590        }
591    }
592
593    pub fn get_unsigned64(&self) -> Option<u64> {
594        match &self.value {
595            AvpValue::Unsigned64(avp) => Some(avp.value()),
596            _ => None,
597        }
598    }
599
600    pub fn get_utf8string(&self) -> Option<&UTF8String> {
601        match &self.value {
602            AvpValue::UTF8String(avp) => Some(avp),
603            _ => None,
604        }
605    }
606
607    pub fn get_octetstring(&self) -> Option<&OctetString> {
608        match &self.value {
609            AvpValue::OctetString(avp) => Some(avp),
610            _ => None,
611        }
612    }
613
614    pub fn get_time(&self) -> Option<&Time> {
615        match &self.value {
616            AvpValue::Time(avp) => Some(avp),
617            _ => None,
618        }
619    }
620
621    pub fn get_float32(&self) -> Option<f32> {
622        match &self.value {
623            AvpValue::Float32(avp) => Some(avp.value()),
624            _ => None,
625        }
626    }
627
628    pub fn get_float64(&self) -> Option<f64> {
629        match &self.value {
630            AvpValue::Float64(avp) => Some(avp.value()),
631            _ => None,
632        }
633    }
634
635    pub fn get_grouped(&self) -> Option<&Grouped> {
636        match &self.value {
637            AvpValue::Grouped(avp) => Some(avp),
638            _ => None,
639        }
640    }
641
642    pub fn fmt(&self, f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
643        let indent = "  ".repeat(depth.max(0));
644
645        let avp_name = self
646            .dict
647            .get_avp_name(self.get_code() as u32, self.get_vendor_id())
648            .unwrap_or("Unknown");
649
650        let avp_name = format!("{}{}", indent, avp_name);
651
652        let vendor_id = match self.get_vendor_id() {
653            Some(v) => v.to_string(),
654            None => "".to_string(),
655        };
656
657        write!(
658            f,
659            "  {:<40} {:>8} {:>5}  {} {} {}  {:<16}  ",
660            avp_name,
661            vendor_id,
662            self.get_code(),
663            get_bool_unicode(self.get_flags().vendor),
664            get_bool_unicode(self.get_flags().mandatory),
665            get_bool_unicode(self.get_flags().private),
666            self.get_value().get_type_name(),
667        )?;
668
669        self.get_value().fmt(f, depth)
670    }
671}
672
673fn get_bool_unicode(v: bool) -> &'static str {
674    if v {
675        "✓"
676    } else {
677        "✗"
678    }
679}
680
681impl fmt::Display for Avp {
682    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683        self.fmt(f, 0)
684    }
685}
686
687#[macro_export]
688macro_rules! avp {
689    ($code:expr, $vendor_id:expr, $flags:expr, $value:expr, $dict:expr $(,)?) => {
690        Avp::new($code, $vendor_id, $flags, $value.into(), $dict)
691    };
692    ($name:expr, $value:expr, $dict:expr $(,)?) => {
693        Avp::from_name($name, $value.into(), $dict)
694    };
695}
696
697#[cfg(test)]
698mod tests {
699    use crate::dictionary;
700    use flags::M;
701
702    use super::*;
703    use std::io::Cursor;
704
705    #[test]
706    fn test_decode_encode_header() {
707        let data = [
708            0x00, 0x00, 0x00, 0x64, // command code
709            0x40, 0x00, 0x00, 0x0C, // flags, length
710        ];
711
712        let mut cursor = Cursor::new(&data);
713        let header = AvpHeader::decode_from(&mut cursor).unwrap();
714
715        assert_eq!(header.code, 100);
716        assert_eq!(header.length, 12);
717        assert_eq!(header.flags.vendor, false);
718        assert_eq!(header.flags.mandatory, true);
719        assert_eq!(header.flags.private, false);
720        assert_eq!(header.vendor_id, None);
721
722        let mut encoded = Vec::new();
723        header.encode_to(&mut encoded).unwrap();
724        assert_eq!(encoded, data);
725    }
726
727    #[test]
728    fn test_decode_encode_header_with_vendor() {
729        let data = [
730            0x00, 0x00, 0x00, 0x64, // command code
731            0x80, 0x00, 0x00, 0x0C, // flags, length
732            0x00, 0x00, 0x00, 0xC8, // vendor_id
733        ];
734
735        let mut cursor = Cursor::new(&data);
736        let header = AvpHeader::decode_from(&mut cursor).unwrap();
737
738        assert_eq!(header.code, 100);
739        assert_eq!(header.length, 12);
740        assert_eq!(header.flags.vendor, true);
741        assert_eq!(header.flags.mandatory, false);
742        assert_eq!(header.flags.private, false);
743        assert_eq!(header.vendor_id, Some(200));
744
745        let mut encoded = Vec::new();
746        header.encode_to(&mut encoded).unwrap();
747        assert_eq!(encoded, data);
748    }
749
750    #[test]
751    fn test_avp_macro() {
752        let dict = Dictionary::new(&[&dictionary::DEFAULT_DICT_XML]);
753        let dict = Arc::new(dict);
754
755        let avp = avp!(
756            264,
757            None,
758            M,
759            Identity::new("host.example.com"),
760            Arc::clone(&dict)
761        );
762        assert_eq!(avp.get_code(), 264);
763        assert_eq!(avp.get_flags().mandatory, true);
764        assert_eq!(avp.get_flags().private, false);
765        assert_eq!(avp.get_flags().vendor, false);
766        assert_eq!(avp.get_vendor_id(), None);
767        assert_eq!(avp.get_identity().unwrap().value(), "host.example.com");
768
769        let avp = avp!(
770            "Session-Id",
771            UTF8String::new("session-id"),
772            Arc::clone(&dict)
773        )
774        .unwrap();
775        assert_eq!(avp.get_code(), 263);
776        assert_eq!(avp.get_flags().mandatory, true);
777        assert_eq!(avp.get_flags().private, false);
778        assert_eq!(avp.get_flags().vendor, false);
779        assert_eq!(avp.get_vendor_id(), None);
780        assert_eq!(avp.get_utf8string().unwrap().value(), "session-id");
781    }
782}