1#![forbid(unsafe_code)]
2use std::{cmp, io::Write};
6
7use crate::{
8 byte_order::{ByteOrder, WriteExt},
9 pcap::file_header::MagicNumberAndEndianness,
10 pcap_ng::PCAP_NG_MAGIC,
11};
12
13pub mod byte_order;
14pub mod link_type;
15pub mod pcap;
16pub mod pcap_ng;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum PcapFileType {
21 Pcap,
25 PcapNg,
27}
28impl PcapFileType {
29 pub fn from_magic(magic: [u8; 4]) -> Option<Self> {
31 if MagicNumberAndEndianness::try_from(magic).is_ok() {
32 Some(PcapFileType::Pcap)
33 } else if magic == PCAP_NG_MAGIC {
34 Some(PcapFileType::PcapNg)
35 } else {
36 None
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct Version {
46 pub major: u16,
48 pub minor: u16,
50}
51impl PartialOrd for Version {
52 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
53 Some(self.cmp(other))
54 }
55}
56impl Ord for Version {
57 fn cmp(&self, other: &Self) -> cmp::Ordering {
58 self.major
59 .cmp(&other.major)
60 .then_with(|| self.minor.cmp(&other.minor))
61 }
62}
63impl Version {
64 pub const PCAP_VERSION_2_4: Version = Version { major: 2, minor: 4 };
66 pub const PCAP_VERSION_2_3: Version = Version { major: 2, minor: 3 };
68 #[inline(always)]
70 pub(crate) fn parse(bytes: &[u8], byte_order: impl ByteOrder) -> Self {
71 let major = byte_order.u16_from_bytes([bytes[0], bytes[1]]);
72 let minor = byte_order.u16_from_bytes([bytes[2], bytes[3]]);
73 Self { major, minor }
74 }
75 pub(crate) fn write<W: Write>(
76 &self,
77 target: &mut W,
78 byte_order: impl ByteOrder,
79 ) -> Result<(), std::io::Error> {
80 target.write_u16(self.major, byte_order)?;
81 target.write_u16(self.minor, byte_order)?;
82 Ok(())
83 }
84}
85
86#[cfg(test)]
87pub(crate) mod test_helpers {
88 use std::{
89 fs::{File, create_dir_all},
90 io::Read,
91 path::{Path, PathBuf},
92 };
93
94 use anyhow::{Result, anyhow};
95
96 use crate::Version;
97
98 pub fn test_target_dir() -> Result<PathBuf> {
99 let base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
100
101 let target_dir = base_dir.join("test_data/actual");
102
103 if !target_dir.exists() {
104 create_dir_all(&target_dir)?;
105 }
106 Ok(target_dir)
107 }
108 pub fn test_expected_dir() -> Result<PathBuf> {
109 let base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
110
111 let target_dir = base_dir.join("test_data/expected");
112
113 if !target_dir.exists() {
114 create_dir_all(&target_dir)?;
115 }
116 Ok(target_dir)
117 }
118
119 pub fn test_files(test_file_name: &str) -> Result<(PathBuf, PathBuf)> {
120 let test_actual = test_target_dir()?.join(test_file_name);
121 let test_expected = test_expected_dir()?.join(test_file_name);
122 Ok((test_actual, test_expected))
123 }
124 #[track_caller]
125 pub fn do_files_match(actual: impl AsRef<Path>, expected: impl AsRef<Path>) -> Result<()> {
126 if !expected.as_ref().exists() {
127 println!("Expected Does not exist coppying actual over");
128 std::fs::copy(actual, expected)?;
129 return Err(anyhow!(
130 "No Expected File existed. But this is just to signal that you a new file was generated"
131 ));
132 }
133 let mut actual_file = File::open(actual)?;
134 let mut expected_file = File::open(expected)?;
135 let mut actual_bytes = Vec::with_capacity(actual_file.metadata()?.len() as usize);
136 let mut expected_bytes = Vec::with_capacity(actual_file.metadata()?.len() as usize);
137
138 actual_file.read_to_end(&mut actual_bytes)?;
139 expected_file.read_to_end(&mut expected_bytes)?;
140
141 assert_eq!(actual_bytes, expected_bytes);
142
143 Ok(())
144 }
145
146 #[test]
147 fn test_version_cmp() {
148 let v2_4 = Version { major: 2, minor: 4 };
149 let v2_3 = Version { major: 2, minor: 3 };
150 let v2_1 = Version { major: 1, minor: 1 };
151
152 assert!(v2_4 > v2_3);
153 assert!(v2_3 > v2_1);
154 assert!(v2_4 > v2_1);
155 }
156}