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