mdbook_linkcheck2/
hashed_regex.rs

1use regex::Regex;
2use serde::{de::Error, Deserialize, Deserializer, Serialize};
3use std::{
4    hash::{Hash, Hasher},
5    ops::Deref,
6    str::FromStr,
7};
8
9/// A wrapper around [`regex::Regex`] which implements **string repr based**
10/// [`Serialize`], [`Deserialize`], [`PartialEq`], [`Eq`], [`Hash`].
11///
12/// It also implements `Deref<Target=Regex>` and [`FromStr`] for convenience.
13///
14/// # Important
15///
16/// **All the implementations are string based**. It means that the said
17/// implementations simply delegate to the underlying implementations for `str`.
18///
19/// For example, while `[0-9]*` and `\d*` are the same regex, they will be
20/// considered different. In particular, the following is true:
21/// ```
22/// use mdbook_linkcheck2::HashedRegex;
23///
24/// assert_ne!(
25///     HashedRegex::new("[0-9]*").unwrap(),
26///     HashedRegex::new(r"\d*").unwrap()
27/// );
28/// ```
29#[derive(Serialize, Debug, Clone)]
30#[serde(transparent)]
31pub struct HashedRegex {
32    /// String representation.
33    pub string: String,
34
35    /// Compiled regexp.
36    #[serde(skip_serializing)]
37    pub re: Regex,
38}
39
40impl HashedRegex {
41    /// Create new [`HashedRegex`] instance.
42    pub fn new(s: &str) -> Result<Self, regex::Error> {
43        let string = s.to_string();
44        let re = Regex::new(s)?;
45
46        Ok(HashedRegex { string, re })
47    }
48}
49
50impl<'de> Deserialize<'de> for HashedRegex {
51    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
52    where
53        D: Deserializer<'de>,
54    {
55        let string = String::deserialize(deserializer)?;
56        let re = Regex::new(&string).map_err(D::Error::custom)?;
57
58        Ok(HashedRegex { string, re })
59    }
60}
61
62impl Hash for HashedRegex {
63    fn hash<H: Hasher>(&self, state: &mut H) {
64        self.string.hash(state);
65    }
66}
67
68impl PartialEq for HashedRegex {
69    fn eq(&self, other: &Self) -> bool {
70        self.string == other.string
71    }
72}
73
74impl Eq for HashedRegex {}
75
76impl FromStr for HashedRegex {
77    type Err = regex::Error;
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        HashedRegex::new(s)
81    }
82}
83
84impl Deref for HashedRegex {
85    type Target = regex::Regex;
86
87    fn deref(&self) -> &regex::Regex {
88        &self.re
89    }
90}