mdbook_linkcheck/
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_linkcheck::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) { self.string.hash(state); }
64}
65
66impl PartialEq for HashedRegex {
67    fn eq(&self, other: &Self) -> bool { self.string == other.string }
68}
69
70impl Eq for HashedRegex {}
71
72impl FromStr for HashedRegex {
73    type Err = regex::Error;
74
75    fn from_str(s: &str) -> Result<Self, Self::Err> { HashedRegex::new(s) }
76}
77
78impl Deref for HashedRegex {
79    type Target = regex::Regex;
80
81    fn deref(&self) -> &regex::Regex { &self.re }
82}