bgpkit_parser/parser/
utils.rs

1/*!
2Provides IO utility functions for read bytes of different length and converting to corresponding structs.
3*/
4use ipnet::{IpNet, Ipv4Net, Ipv6Net};
5use std::convert::TryFrom;
6use std::{
7    io,
8    net::{Ipv4Addr, Ipv6Addr},
9};
10
11use crate::error::ParserError;
12use crate::models::*;
13use crate::ParserError::TruncatedMsg;
14use bytes::{Buf, BufMut, Bytes, BytesMut};
15use log::debug;
16use regex::Regex;
17use std::net::IpAddr;
18
19impl ReadUtils for Bytes {}
20
21// Allow reading IPs from Reads
22pub trait ReadUtils: Buf {
23    #[inline]
24    fn has_n_remaining(&self, n: usize) -> Result<(), ParserError> {
25        let remaining = self.remaining();
26        if remaining < n {
27            Err(TruncatedMsg(format!(
28                "not enough bytes to read. remaining: {}, required: {}",
29                remaining, n
30            )))
31        } else {
32            Ok(())
33        }
34    }
35
36    #[inline]
37    fn read_u8(&mut self) -> Result<u8, ParserError> {
38        self.has_n_remaining(1)?;
39        Ok(self.get_u8())
40    }
41
42    #[inline]
43    fn read_u16(&mut self) -> Result<u16, ParserError> {
44        self.has_n_remaining(2)?;
45        Ok(self.get_u16())
46    }
47
48    #[inline]
49    fn read_u32(&mut self) -> Result<u32, ParserError> {
50        self.has_n_remaining(4)?;
51        Ok(self.get_u32())
52    }
53
54    #[inline]
55    fn read_u64(&mut self) -> Result<u64, ParserError> {
56        self.has_n_remaining(8)?;
57        Ok(self.get_u64())
58    }
59
60    fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ParserError> {
61        self.has_n_remaining(buf.len())?;
62        self.copy_to_slice(buf);
63        Ok(())
64    }
65
66    fn read_address(&mut self, afi: &Afi) -> io::Result<IpAddr> {
67        match afi {
68            Afi::Ipv4 => match self.read_ipv4_address() {
69                Ok(ip) => Ok(IpAddr::V4(ip)),
70                _ => Err(io::Error::other("Cannot parse IPv4 address")),
71            },
72            Afi::Ipv6 => match self.read_ipv6_address() {
73                Ok(ip) => Ok(IpAddr::V6(ip)),
74                _ => Err(io::Error::other("Cannot parse IPv6 address")),
75            },
76        }
77    }
78
79    fn read_ipv4_address(&mut self) -> Result<Ipv4Addr, ParserError> {
80        let addr = self.read_u32()?;
81        Ok(Ipv4Addr::from(addr))
82    }
83
84    fn read_ipv6_address(&mut self) -> Result<Ipv6Addr, ParserError> {
85        self.has_n_remaining(16)?;
86        let buf = self.get_u128();
87        Ok(Ipv6Addr::from(buf))
88    }
89
90    fn read_ipv4_prefix(&mut self) -> Result<Ipv4Net, ParserError> {
91        let addr = self.read_ipv4_address()?;
92        let mask = self.read_u8()?;
93        match Ipv4Net::new(addr, mask) {
94            Ok(n) => Ok(n),
95            Err(_) => Err(io::Error::other("Invalid prefix mask").into()),
96        }
97    }
98
99    fn read_ipv6_prefix(&mut self) -> Result<Ipv6Net, ParserError> {
100        let addr = self.read_ipv6_address()?;
101        let mask = self.read_u8()?;
102        match Ipv6Net::new(addr, mask) {
103            Ok(n) => Ok(n),
104            Err(_) => Err(io::Error::other("Invalid prefix mask").into()),
105        }
106    }
107
108    #[inline]
109    fn read_asn(&mut self, as_length: AsnLength) -> Result<Asn, ParserError> {
110        match as_length {
111            AsnLength::Bits16 => self.read_u16().map(Asn::new_16bit),
112            AsnLength::Bits32 => self.read_u32().map(Asn::new_32bit),
113        }
114    }
115
116    fn read_asns(&mut self, as_length: &AsnLength, count: usize) -> Result<Vec<Asn>, ParserError> {
117        let mut path = Vec::with_capacity(count);
118
119        match as_length {
120            AsnLength::Bits16 => {
121                self.has_n_remaining(count * 2)?; // 2 bytes for 16-bit ASN
122                for _ in 0..count {
123                    path.push(Asn::new_16bit(self.read_u16()?));
124                }
125            }
126            AsnLength::Bits32 => {
127                self.has_n_remaining(count * 4)?; // 4 bytes for 32-bit ASN
128                for _ in 0..count {
129                    path.push(Asn::new_32bit(self.read_u32()?));
130                }
131            }
132        }
133
134        Ok(path)
135    }
136
137    fn read_afi(&mut self) -> Result<Afi, ParserError> {
138        Afi::try_from(self.read_u16()?).map_err(ParserError::from)
139    }
140
141    fn read_safi(&mut self) -> Result<Safi, ParserError> {
142        Safi::try_from(self.read_u8()?).map_err(ParserError::from)
143    }
144
145    /// Read announced/withdrawn prefix.
146    ///
147    /// The length in bits is 1 byte, and then based on the IP version it reads different number of bytes.
148    /// If the `add_path` is true, it will also first read a 4-byte path id first; otherwise, a path-id of 0
149    /// is automatically set.
150    fn read_nlri_prefix(
151        &mut self,
152        afi: &Afi,
153        add_path: bool,
154    ) -> Result<NetworkPrefix, ParserError> {
155        let path_id = if add_path { self.read_u32()? } else { 0 };
156
157        // Length in bits
158        let bit_len = self.read_u8()?;
159
160        // Convert to bytes
161        let byte_len: usize = (bit_len as usize).div_ceil(8);
162        let addr: IpAddr = match afi {
163            Afi::Ipv4 => {
164                // 4 bytes -- u32
165                if byte_len > 4 {
166                    return Err(ParserError::ParseError(format!(
167                        "Invalid byte length for IPv4 prefix. byte_len: {}, bit_len: {}",
168                        byte_len, bit_len
169                    )));
170                }
171                let mut buff = [0; 4];
172                self.has_n_remaining(byte_len)?;
173                for i in 0..byte_len {
174                    buff[i] = self.get_u8();
175                }
176                IpAddr::V4(Ipv4Addr::from(buff))
177            }
178            Afi::Ipv6 => {
179                // 16 bytes
180                if byte_len > 16 {
181                    return Err(ParserError::ParseError(format!(
182                        "Invalid byte length for IPv6 prefix. byte_len: {}, bit_len: {}",
183                        byte_len, bit_len
184                    )));
185                }
186                self.has_n_remaining(byte_len)?;
187                let mut buff = [0; 16];
188                for i in 0..byte_len {
189                    buff[i] = self.get_u8();
190                }
191                IpAddr::V6(Ipv6Addr::from(buff))
192            }
193        };
194        let prefix = match IpNet::new(addr, bit_len) {
195            Ok(p) => p,
196            Err(_) => {
197                return Err(ParserError::ParseError(format!(
198                    "Invalid network prefix length: {}",
199                    bit_len
200                )))
201            }
202        };
203
204        Ok(NetworkPrefix::new(prefix, path_id))
205    }
206
207    fn read_n_bytes(&mut self, n_bytes: usize) -> Result<Vec<u8>, ParserError> {
208        self.has_n_remaining(n_bytes)?;
209        Ok(self.copy_to_bytes(n_bytes).into())
210    }
211
212    fn read_n_bytes_to_string(&mut self, n_bytes: usize) -> Result<String, ParserError> {
213        let buffer = self.read_n_bytes(n_bytes)?;
214        Ok(buffer
215            .into_iter()
216            .map(|x: u8| x as char)
217            .collect::<String>())
218    }
219}
220
221pub fn parse_nlri_list(
222    mut input: Bytes,
223    add_path: bool,
224    afi: &Afi,
225) -> Result<Vec<NetworkPrefix>, ParserError> {
226    let mut is_add_path = add_path;
227    let mut prefixes = vec![];
228
229    let mut retry = false;
230    let mut guessed = false;
231
232    let mut input_copy = None;
233
234    while input.remaining() > 0 {
235        if !is_add_path && input[0] == 0 {
236            // it's likely that this is a add-path wrongfully wrapped in non-add-path msg
237            debug!("not add-path but with NLRI size to be 0, likely add-path msg in wrong msg type, treat as add-path now");
238            // cloning the data bytes
239            is_add_path = true;
240            guessed = true;
241            input_copy = Some(input.clone());
242        }
243        let prefix = match input.read_nlri_prefix(afi, is_add_path) {
244            Ok(p) => p,
245            Err(e) => {
246                if guessed {
247                    retry = true;
248                    break;
249                } else {
250                    return Err(e);
251                }
252            }
253        };
254        prefixes.push(prefix);
255    }
256
257    if retry {
258        prefixes.clear();
259        // try again without attempt to guess add-path
260        // if we reach here (retry==true), input_copy must be Some
261        let mut input_2 = input_copy.unwrap();
262        while input_2.remaining() > 0 {
263            let prefix = input_2.read_nlri_prefix(afi, add_path)?;
264            prefixes.push(prefix);
265        }
266    }
267
268    Ok(prefixes)
269}
270
271pub fn encode_asn(asn: &Asn, asn_len: &AsnLength) -> Bytes {
272    let mut bytes = BytesMut::new();
273    match asn_len {
274        AsnLength::Bits16 => bytes.put_u16(asn.into()),
275        AsnLength::Bits32 => {
276            bytes.put_u32(asn.into());
277        }
278    }
279    bytes.freeze()
280}
281
282pub fn encode_ipaddr(addr: &IpAddr) -> Vec<u8> {
283    match addr {
284        IpAddr::V4(addr) => addr.octets().to_vec(),
285        IpAddr::V6(addr) => addr.octets().to_vec(),
286    }
287}
288
289pub fn encode_nlri_prefixes(prefixes: &[NetworkPrefix], add_path: bool) -> Bytes {
290    let mut bytes = BytesMut::new();
291    for prefix in prefixes {
292        bytes.extend(prefix.encode(add_path));
293    }
294    bytes.freeze()
295}
296
297/// A CRC32 implementation that converts a string to a hex string.
298///
299/// CRC32 is a checksum algorithm that is used to verify the integrity of data. It is short in
300/// length and sufficient for generating unique file names based on remote URLs.
301pub fn crc32(input: &str) -> String {
302    let input_bytes = input.as_bytes();
303    let mut table = [0u32; 256];
304    let polynomial = 0xedb88320u32;
305
306    for i in 0..256 {
307        let mut crc = i as u32;
308        for _ in 0..8 {
309            if crc & 1 == 1 {
310                crc = (crc >> 1) ^ polynomial;
311            } else {
312                crc >>= 1;
313            }
314        }
315        table[i as usize] = crc;
316    }
317
318    let mut crc = !0u32;
319    for byte in input_bytes.iter() {
320        let index = ((crc ^ (*byte as u32)) & 0xff) as usize;
321        crc = (crc >> 8) ^ table[index];
322    }
323
324    format!("{:08x}", !crc)
325}
326
327/// Convert a f64 timestamp into u32 seconds and u32 microseconds.
328///
329/// # Arguments
330///
331/// * `timestamp` - The timestamp to convert.
332///
333/// # Returns
334///
335/// A tuple containing the converted seconds and microseconds.
336///
337/// # Example
338///
339/// ```rust
340/// use bgpkit_parser::utils::convert_timestamp;
341///
342/// let timestamp = 1609459200.123456;
343/// let (seconds, microseconds) = convert_timestamp(timestamp);
344/// assert_eq!(seconds, 1609459200);
345/// assert_eq!(microseconds, 123456);
346/// ```
347// convert f64 timestamp into u32 seconds and u32 microseconds
348pub fn convert_timestamp(timestamp: f64) -> (u32, u32) {
349    let seconds = timestamp as u32;
350    let microseconds = ((timestamp - seconds as f64) * 1_000_000.0) as u32;
351    (seconds, microseconds)
352}
353
354#[derive(Debug, Clone)]
355pub struct ComparableRegex {
356    pattern: String,
357    regex: Regex,
358}
359
360impl PartialEq for ComparableRegex {
361    fn eq(&self, other: &Self) -> bool {
362        self.pattern == other.pattern
363    }
364}
365
366impl ComparableRegex {
367    pub fn new(pattern: &str) -> Result<Self, ParserError> {
368        let regex = match Regex::new(pattern) {
369            Ok(r) => r,
370            Err(_) => {
371                return Err(ParserError::FilterError(format!(
372                    "Invalid regex pattern: {}",
373                    pattern
374                )))
375            }
376        };
377        Ok(ComparableRegex {
378            pattern: pattern.to_string(),
379            regex,
380        })
381    }
382
383    pub fn is_match<S: AsRef<str>>(&self, text: S) -> bool {
384        self.regex.is_match(text.as_ref())
385    }
386}
387
388#[cfg(test)]
389mod tests {
390    use super::*;
391    use bytes::Bytes;
392
393    #[test]
394    fn test_read_u8() {
395        let mut buf = Bytes::from_static(&[0x12]);
396        assert_eq!(buf.read_u8().unwrap(), 0x12);
397    }
398
399    #[test]
400    fn test_read_u16() {
401        let mut buf = Bytes::from_static(&[0x12, 0x34]);
402        assert_eq!(buf.read_u16().unwrap(), 0x1234);
403    }
404
405    #[test]
406    fn test_read_u32() {
407        let mut buf = Bytes::from_static(&[0x12, 0x34, 0x56, 0x78]);
408        assert_eq!(buf.read_u32().unwrap(), 0x12345678);
409    }
410
411    #[test]
412    fn test_read_u64() {
413        let mut buf = Bytes::from_static(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]);
414        assert_eq!(buf.read_u64().unwrap(), 0x123456789ABCDEF0);
415    }
416
417    #[test]
418    fn test_read_ipv4_address() {
419        let mut buf = Bytes::from_static(&[0xC0, 0xA8, 0x01, 0x01]);
420        assert_eq!(
421            buf.read_ipv4_address().unwrap(),
422            Ipv4Addr::new(192, 168, 1, 1)
423        );
424    }
425
426    #[test]
427    fn test_read_ipv6_address() {
428        let mut buf = Bytes::from_static(&[
429            0x20, 0x01, 0x0D, 0xB8, 0x85, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x2E, 0x03, 0x70,
430            0x73, 0x34,
431        ]);
432        assert_eq!(
433            buf.read_ipv6_address().unwrap(),
434            Ipv6Addr::new(0x2001, 0x0DB8, 0x85A3, 0x0000, 0x0000, 0x8A2E, 0x0370, 0x7334)
435        );
436    }
437
438    #[test]
439    fn test_read_address() {
440        let mut buf = Bytes::from_static(&[0xC0, 0xA8, 0x01, 0x01]);
441        assert_eq!(
442            buf.read_address(&Afi::Ipv4).unwrap(),
443            IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))
444        );
445
446        let mut buf = Bytes::from_static(&[
447            0x20, 0x01, 0x0D, 0xB8, 0x85, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x2E, 0x03, 0x70,
448            0x73, 0x34,
449        ]);
450        assert_eq!(
451            buf.read_address(&Afi::Ipv6).unwrap(),
452            IpAddr::V6(Ipv6Addr::new(
453                0x2001, 0x0DB8, 0x85A3, 0x0000, 0x0000, 0x8A2E, 0x0370, 0x7334
454            ))
455        );
456
457        let mut buf = Bytes::from_static(&[0xC0, 0xA8, 0x01]);
458        assert!(buf.read_address(&Afi::Ipv4).is_err());
459
460        let mut buf = Bytes::from_static(&[
461            0x20, 0x01, 0x0D, 0xB8, 0x85, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x2E, 0x03, 0x70,
462            0x73,
463        ]);
464        assert!(buf.read_address(&Afi::Ipv6).is_err());
465    }
466
467    #[test]
468    fn test_read_asn() {
469        let mut buf = Bytes::from_static(&[0x00, 0x01]);
470        assert_eq!(buf.read_asn(AsnLength::Bits16).unwrap(), Asn::new_16bit(1));
471
472        let mut buf = Bytes::from_static(&[0x00, 0x00, 0x01, 0x00]);
473        assert_eq!(
474            buf.read_asn(AsnLength::Bits32).unwrap(),
475            Asn::new_32bit(256)
476        );
477    }
478
479    #[test]
480    fn read_asns() {
481        let mut buf = Bytes::from_static(&[0x00, 0x01, 0x00, 0x00]);
482        assert_eq!(
483            buf.read_asns(&AsnLength::Bits16, 2).unwrap(),
484            vec![Asn::new_16bit(1), Asn::new_16bit(0)]
485        );
486    }
487
488    #[test]
489    fn test_read_afi() {
490        let mut buf = Bytes::from_static(&[0x00, 0x01]);
491        assert_eq!(buf.read_afi().unwrap(), Afi::Ipv4);
492
493        let mut buf = Bytes::from_static(&[0x00, 0x02]);
494        assert_eq!(buf.read_afi().unwrap(), Afi::Ipv6);
495    }
496
497    #[test]
498    fn test_read_safi() {
499        let mut buf = Bytes::from_static(&[0x01]);
500        assert_eq!(buf.read_safi().unwrap(), Safi::Unicast);
501
502        let mut buf = Bytes::from_static(&[0x02]);
503        assert_eq!(buf.read_safi().unwrap(), Safi::Multicast);
504    }
505
506    #[test]
507    fn test_has_n_remaining() {
508        let mut buf = Bytes::from_static(&[0x12, 0x34, 0x56, 0x78]);
509        assert!(buf.has_n_remaining(4).is_ok());
510        assert!(buf.has_n_remaining(5).is_err());
511
512        let _ = buf.read_u8().unwrap();
513        assert!(buf.has_n_remaining(3).is_ok());
514        assert!(buf.has_n_remaining(4).is_err());
515    }
516
517    #[test]
518    fn test_read_ipv4_prefix() {
519        let mut buf = Bytes::from_static(&[0xC0, 0xA8, 0x01, 0x01, 0x18]);
520        assert_eq!(
521            buf.read_ipv4_prefix().unwrap(),
522            Ipv4Net::new(Ipv4Addr::new(192, 168, 1, 1), 24).unwrap()
523        );
524
525        // Test with invalid IPv4 prefix mask, /33
526        let mut buf = Bytes::from_static(&[0xC0, 0xA8, 0x01, 0x01, 0x21]);
527        assert!(buf.read_ipv4_prefix().is_err());
528    }
529
530    #[test]
531    fn test_read_ipv6_prefix() {
532        let mut buf = Bytes::from_static(&[
533            0x20, 0x01, 0x0D, 0xB8, 0x85, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x2E, 0x03, 0x70,
534            0x73, 0x34, 0x40,
535        ]);
536        assert_eq!(
537            buf.read_ipv6_prefix().unwrap(),
538            Ipv6Net::new(
539                Ipv6Addr::new(0x2001, 0x0DB8, 0x85A3, 0x0000, 0x0000, 0x8A2E, 0x0370, 0x7334),
540                64
541            )
542            .unwrap()
543        );
544
545        // Test with invalid IPv6 prefix mask, /129
546        let mut buf = Bytes::from_static(&[
547            0x20, 0x01, 0x0D, 0xB8, 0x85, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x2E, 0x03, 0x70,
548            0x73, 0x34, 0x81,
549        ]);
550        assert!(buf.read_ipv6_prefix().is_err());
551    }
552
553    #[test]
554    fn test_read_n_bytes() {
555        let mut buf = Bytes::from_static(&[0x12, 0x34, 0x56, 0x78]);
556        assert_eq!(buf.read_n_bytes(4).unwrap(), vec![0x12, 0x34, 0x56, 0x78]);
557    }
558
559    #[test]
560    fn test_read_n_bytes_to_string() {
561        let mut buf = Bytes::from_static(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]); // "Hello" in ASCII
562        assert_eq!(buf.read_n_bytes_to_string(5).unwrap(), "Hello");
563    }
564
565    #[test]
566    fn test_crc32() {
567        assert_eq!(crc32("Hello, World!"), "ec4ac3d0");
568    }
569
570    #[test]
571    fn test_read_nlri_prefix() {
572        let mut buf = Bytes::from_static(&[0x18, 0xC0, 0xA8, 0x01]);
573        let expected = NetworkPrefix::new(
574            IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 1, 0), 24).unwrap()),
575            0,
576        );
577        assert_eq!(buf.read_nlri_prefix(&Afi::Ipv4, false).unwrap(), expected);
578
579        let mut buf = Bytes::from_static(&[0x00, 0x00, 0x00, 0x01, 0x18, 0xC0, 0xA8, 0x01]);
580        let expected = NetworkPrefix::new(
581            IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 1, 0), 24).unwrap()),
582            1,
583        );
584        assert_eq!(buf.read_nlri_prefix(&Afi::Ipv4, true).unwrap(), expected);
585    }
586
587    #[test]
588    fn test_encode_asn() {
589        let asn = Asn::new_32bit(1);
590        let asn_len = AsnLength::Bits32;
591        let expected = Bytes::from_static(&[0x00, 0x00, 0x00, 0x01]);
592        assert_eq!(encode_asn(&asn, &asn_len), expected);
593
594        let asn = Asn::new_16bit(1);
595        let asn_len = AsnLength::Bits16;
596        let expected = Bytes::from_static(&[0x00, 0x01]);
597        assert_eq!(encode_asn(&asn, &asn_len), expected);
598    }
599
600    #[test]
601    fn test_encode_ipaddr() {
602        let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
603        let expected = vec![192, 168, 1, 1];
604        assert_eq!(encode_ipaddr(&addr), expected);
605
606        let addr = IpAddr::V6(Ipv6Addr::new(
607            0x2001, 0x0DB8, 0x85A3, 0x0000, 0x0000, 0x8A2E, 0x0370, 0x7334,
608        ));
609        let expected = vec![
610            0x20, 0x01, 0x0D, 0xB8, 0x85, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x2E, 0x03, 0x70,
611            0x73, 0x34,
612        ];
613        assert_eq!(encode_ipaddr(&addr), expected);
614    }
615
616    #[test]
617    fn test_encode_nlri_prefixes() {
618        let prefixes = vec![
619            NetworkPrefix::new(
620                IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 1, 0), 24).unwrap()),
621                0,
622            ),
623            NetworkPrefix::new(
624                IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 2, 0), 24).unwrap()),
625                0,
626            ),
627        ];
628        let expected = Bytes::from_static(&[0x18, 0xC0, 0xA8, 0x01, 0x18, 0xC0, 0xA8, 0x02]);
629        assert_eq!(encode_nlri_prefixes(&prefixes, false), expected);
630
631        let prefixes = vec![
632            NetworkPrefix::new(
633                IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 1, 0), 24).unwrap()),
634                1,
635            ),
636            NetworkPrefix::new(
637                IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 2, 0), 24).unwrap()),
638                1,
639            ),
640        ];
641        let expected = Bytes::from_static(&[
642            0x00, 0x00, 0x00, 0x01, 0x18, 0xC0, 0xA8, 0x01, 0x00, 0x00, 0x00, 0x01, 0x18, 0xC0,
643            0xA8, 0x02,
644        ]);
645        assert_eq!(encode_nlri_prefixes(&prefixes, true), expected);
646    }
647
648    #[test]
649    fn test_comparable_regex_functionality() {
650        // Test valid pattern creation
651        let regex1 = ComparableRegex::new(r"\d+").unwrap();
652        let regex2 = ComparableRegex::new(r"\d+").unwrap();
653        let regex3 = ComparableRegex::new(r"\w+").unwrap();
654
655        // Verify pattern storage
656        assert_eq!(regex1.pattern, r"\d+");
657        assert_eq!(regex3.pattern, r"\w+");
658
659        // Verify equality with same pattern
660        assert_eq!(regex1, regex2);
661
662        // Verify inequality with different patterns
663        assert_ne!(regex1, regex3);
664
665        // Verify regex matching functionality
666        assert!(regex1.regex.is_match("123"));
667        assert!(!regex1.regex.is_match("abc"));
668    }
669
670    #[test]
671    #[should_panic(expected = "Invalid regex pattern")]
672    fn test_comparable_regex_invalid_pattern_panic() {
673        // Test invalid pattern creation
674        ComparableRegex::new(r"(\d+").unwrap(); // Unclosed parenthesis should panic
675    }
676}