commit e64e9a57bf7ee0554ed1bfe21d49a1ed0a6124f3
parent 4fa6b9277beeff563a195796b4d0a6f016234b36
Author: Zack Newman <zack@philomathiclife.com>
Date: Mon, 5 Feb 2024 11:36:48 -0700
make domain more compact and simpler
Diffstat:
M | Cargo.toml | | | 2 | +- |
M | src/char_set.rs | | | 318 | ++++++++++++++++++++++++++++++------------------------------------------------- |
M | src/dom.rs | | | 660 | ++++++++++++++++++------------------------------------------------------------- |
3 files changed, 272 insertions(+), 708 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
name = "ascii_domain"
readme = "README.md"
repository = "https://git.philomathiclife.com/repos/ascii_domain/"
-version = "0.2.0"
+version = "0.3.0"
[lib]
name = "ascii_domain"
diff --git a/src/char_set.rs b/src/char_set.rs
@@ -1,19 +1,15 @@
use core::{
- array::IntoIter,
- borrow::Borrow,
fmt::{self, Display, Formatter},
- ops::Deref,
- slice::Iter,
str,
};
use std::error::Error;
-/// Error returned from [`AllowedAscii::try_from`].
+/// Error returned from [`AllowedAscii::try_from_unique_ascii`].
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum AsciiErr {
- /// Since `AllowedAscii` only allows unique ASCII characters, the maximum `COUNT` is 128.
- /// This variant is returned when the `COUNT` exceeds that.
- CountTooLarge,
+ /// Since `AllowedAscii` only allows unique ASCII characters and doesn't allow `b'.'`, the maximum count is
+ /// 127. This variant is returned when the count exceeds that.
+ CountTooLarge(usize),
/// The contained `u8` is not valid ASCII (i.e., it is strictly greater than 127).
InvalidByte(u8),
/// `b'.'` was in the allowed ASCII. It is the only ASCII value not allowed since it is always used
@@ -27,7 +23,9 @@ impl Display for AsciiErr {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
- Self::CountTooLarge => f.write_str("allowed ASCII is larger than 128"),
+ Self::CountTooLarge(byt) => {
+ write!(f, "the allowed ASCII had {byt} values, but 127 is the max")
+ }
Self::InvalidByte(byt) => {
write!(f, "allowed ASCII was passed the invalid byte value {byt}")
}
@@ -44,116 +42,130 @@ impl Display for AsciiErr {
}
}
impl Error for AsciiErr {}
-// This can be large so we don't implement `Copy`.
-/// Container of the `COUNT` ASCII `u8`s that are allowed to appear in a [`crate::dom::Label`].
-/// Note that while [`crate::dom::Domain`] treats ASCII uppercase letters as lowercase,
-/// it still depends on such `u8`s being included. For example if `b'A'` is not
-/// included, then `b'A'` is not allowed even if `b'a'` is included.
+/// Container of the ASCII `u8`s that are allowed to appear in a [`crate::dom::Label`]. Note that while
+/// [`crate::dom::Domain`] treats ASCII uppercase letters as lowercase, it still depends on such `u8`s being
+/// included. For example if `b'A'` is not included, then `b'A'` is not allowed even if `b'a'` is included.
///
/// It is _highly_ unlikely that non-printable ASCII nor `b'\\'` should be used since such ASCII would almost
/// certainly require being escaped.
-#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
-pub struct AllowedAscii<const COUNT: usize> {
+pub struct AllowedAscii<T> {
/// The allowed ASCII `u8`s.
- allowed: [u8; COUNT],
+ allowed: T,
}
-impl<const COUNT: usize> AllowedAscii<COUNT> {
- /// Returns `true` iff `val` is an allowed ASCII value in a [`crate::dom::Label`].
+impl<T> AllowedAscii<T> {
+ /// Returns a reference to the contained value.
///
/// # Example
///
/// ```
/// use ascii_domain::char_set;
- /// assert!(char_set::ASCII_LETTERS.contains(b'a'));
+ /// assert!(char_set::ASCII_LETTERS.as_inner().len() == 52);
/// ```
#[inline]
- #[must_use]
- pub fn contains(&self, val: u8) -> bool {
- self.allowed.binary_search(&val).is_ok()
+ pub const fn as_inner(&self) -> &T {
+ &self.allowed
}
- /// Returns an [`Iterator`] of the allowed ASCII `u8`s without consuming `self`.
+ /// Returns the contained value consuming `self`.
///
/// # Example
///
/// ```
/// use ascii_domain::char_set;
- /// assert!(char_set::ASCII_LETTERS.into_iter().next() == Some(b'A'));
+ /// assert!(char_set::ASCII_LETTERS.into_inner().len() == 52);
/// ```
#[inline]
- pub fn iter(&self) -> Iter<'_, u8> {
- self.into_iter()
- }
-}
-impl<const COUNT: usize> AsRef<[u8]> for AllowedAscii<COUNT> {
- #[inline]
- fn as_ref(&self) -> &[u8] {
- self.allowed.as_slice()
- }
-}
-impl<const COUNT: usize> AsRef<str> for AllowedAscii<COUNT> {
- #[allow(unsafe_code)]
- #[inline]
- fn as_ref(&self) -> &str {
- // SAFETY:
- // `allowed` only contains ASCII which is a valid subset of `str`s.
- // `AllowedAscii` does not publicly expose `allowed` and is immutable
- // so this invariant will hold.
- unsafe { str::from_utf8_unchecked(self.allowed.as_slice()) }
+ pub fn into_inner(self) -> T {
+ self.allowed
}
}
-impl<const COUNT: usize> Borrow<[u8]> for AllowedAscii<COUNT> {
- #[inline]
- fn borrow(&self) -> &[u8] {
- self.allowed.as_slice()
- }
-}
-impl<const COUNT: usize> Borrow<str> for AllowedAscii<COUNT> {
+impl<T: AsRef<[u8]>> AllowedAscii<T> {
+ /// Returns `true` iff `val` is an allowed ASCII value in a [`crate::dom::Label`].
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use ascii_domain::char_set;
+ /// assert!(char_set::ASCII_LETTERS.contains(b'a'));
+ /// ```
#[inline]
- fn borrow(&self) -> &str {
- self.as_ref()
+ #[must_use]
+ pub fn contains(&self, val: u8) -> bool {
+ // We sort `allowed` in `try_from_unique_ascii`, so `binary_search` is fine.
+ self.allowed.as_ref().binary_search(&val).is_ok()
}
-}
-impl<const COUNT: usize> Deref for AllowedAscii<COUNT> {
- type Target = [u8];
+ /// Returns the number of allowed ASCII characters.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use ascii_domain::char_set;
+ /// assert!(char_set::ASCII_LETTERS.len() == 52);
+ /// ```
+ #[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
#[inline]
- fn deref(&self) -> &Self::Target {
- self.allowed.as_slice()
+ #[must_use]
+ pub fn len(&self) -> u8 {
+ // We enforce only unique non `b'.'` ASCII in `try_from_unique_ascii` which among other things means
+ // the max count is 127 so truncation will not occur
+ self.allowed.as_ref().len() as u8
}
-}
-impl<const COUNT: usize> From<AllowedAscii<COUNT>> for [u8; COUNT] {
+ /// Returns `true` iff `self` does not contain any ASCII.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ascii_domain::char_set::{self, AllowedAscii};
+ /// assert!(!char_set::ASCII_LETTERS.is_empty());
+ /// assert!(AllowedAscii::try_from_unique_ascii([]).unwrap().is_empty());
+ /// ```
#[inline]
- fn from(value: AllowedAscii<COUNT>) -> Self {
- value.allowed
+ #[must_use]
+ pub fn is_empty(&self) -> bool {
+ self.allowed.as_ref().is_empty()
}
}
-impl<const COUNT: usize> TryFrom<[u8; COUNT]> for AllowedAscii<COUNT> {
- type Error = AsciiErr;
- /// `allowed` must contain unique ASCII `u8`s. Note it is likely
- /// `allowed` should be a subset of [`PRINTABLE_ASCII`] since any other ASCII
- /// would likely require some form of escape character logic. Additionally,
- /// it is likely `COUNT` should be greater than 0; otherwise the returned `AllowedAscii`
- /// will always cause [`crate::dom::Domain::try_from_bytes`] to error since `Domain` requires
+impl<T: AsMut<[u8]>> AllowedAscii<T> {
+ /// `allowed` must contain unique ASCII `u8`s. Note it is likely `allowed` should be a subset of
+ /// [`PRINTABLE_ASCII`] since any other ASCII would likely require some form of escape character logic.
+ /// Additionally, it is likely `allowed.as_mut().len()` should be greater than 0; otherwise the returned
+ /// `AllowedAscii` will always cause [`crate::dom::Domain::try_from_bytes`] to error since `Domain` requires
/// at least one non-root [`crate::dom::Label`].
+ ///
+ /// `allowed` is mutated such that `allowed.as_mut()` is sorted in order.
+ ///
+ /// # Errors
+ ///
+ /// Returns `AsciiError` iff `allowed` does not contain a set of unique ASCII `u8`s or contains `b'.'`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ascii_domain::char_set::{AllowedAscii, AsciiErr};
+ /// assert!(AllowedAscii::try_from_unique_ascii(b"asdfghjkl".to_owned()).map_or(false, |ascii| ascii.contains(b'a') && !ascii.contains(b'A')));
+ /// assert!(AllowedAscii::try_from_unique_ascii(b"aa".to_owned()).map_or_else(|err| err == AsciiErr::Duplicate(b'a'), |_| false));
+ /// assert!(AllowedAscii::try_from_unique_ascii([255]).map_or_else(|err| err == AsciiErr::InvalidByte(255), |_| false));
+ /// assert!(AllowedAscii::try_from_unique_ascii([0; 129]).map_or_else(|err| err == AsciiErr::CountTooLarge(129), |_| false));
+ /// assert!(AllowedAscii::try_from_unique_ascii([b'.']).map_or_else(|err| err == AsciiErr::Contains46, |_| false));
+ /// ```
#[inline]
- fn try_from(mut allowed: [u8; COUNT]) -> Result<Self, Self::Error> {
- if COUNT > 128 {
- Err(AsciiErr::CountTooLarge)
+ pub fn try_from_unique_ascii(mut allowed: T) -> Result<Self, AsciiErr> {
+ let bytes = allowed.as_mut();
+ if bytes.len() > 127 {
+ Err(AsciiErr::CountTooLarge(bytes.len()))
} else {
- // We must sort in order for `contains` to use `binary_search`.
- allowed.sort_unstable();
- // Since `allowed` is sorted, we simply have to check the last value
- // to determine if valid ASCII was provided.
- if let Some(byt) = allowed.last() {
+ bytes.sort_unstable();
+ // Since `bytes` is sorted, we simply have to check the last value to determine if valid ASCII was
+ // provided.
+ if let Some(byt) = bytes.last() {
let b = *byt;
if b > 127 {
return Err(AsciiErr::InvalidByte(b));
}
}
- allowed
+ bytes
.iter()
- // Since `b'.'` is not allowed, we can use it as an initializer.
- // Note this means `byt == b'.'` must be the first if-branch.
- .try_fold(b'.', |prev, b| {
+ // 255 is not valid ASCII, so we can use it as an initializer.
+ .try_fold(255, |prev, b| {
let byt = *b;
if byt == b'.' {
Err(AsciiErr::Contains46)
@@ -167,110 +179,10 @@ impl<const COUNT: usize> TryFrom<[u8; COUNT]> for AllowedAscii<COUNT> {
}
}
}
-impl<const COUNT: usize> IntoIterator for AllowedAscii<COUNT> {
- type Item = u8;
- type IntoIter = IntoIter<u8, COUNT>;
- #[inline]
- fn into_iter(self) -> Self::IntoIter {
- self.allowed.into_iter()
- }
-}
-impl<'a, const COUNT: usize> IntoIterator for &'a AllowedAscii<COUNT> {
- type Item = &'a u8;
- type IntoIter = Iter<'a, u8>;
- #[inline]
- fn into_iter(self) -> Self::IntoIter {
- self.allowed.iter()
- }
-}
-impl<const COUNT: usize> PartialEq<AllowedAscii<COUNT>> for &AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &AllowedAscii<COUNT>) -> bool {
- **self == *other
- }
-}
-impl<const COUNT: usize> PartialEq<&Self> for AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &&Self) -> bool {
- *self == **other
- }
-}
-impl<const COUNT: usize> PartialEq<[u8]> for AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &[u8]) -> bool {
- *self.allowed.as_slice() == *other
- }
-}
-impl<const COUNT: usize> PartialEq<AllowedAscii<COUNT>> for [u8] {
- #[inline]
- fn eq(&self, other: &AllowedAscii<COUNT>) -> bool {
- *self == *other.allowed.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<[u8]> for &AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &[u8]) -> bool {
- *self.allowed.as_slice() == *other
- }
-}
-impl<const COUNT: usize> PartialEq<&AllowedAscii<COUNT>> for [u8] {
- #[inline]
- fn eq(&self, other: &&AllowedAscii<COUNT>) -> bool {
- *self == *other.allowed.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<&[u8]> for AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &&[u8]) -> bool {
- *self.allowed.as_slice() == **other
- }
-}
-impl<const COUNT: usize> PartialEq<AllowedAscii<COUNT>> for &[u8] {
- #[inline]
- fn eq(&self, other: &AllowedAscii<COUNT>) -> bool {
- **self == *other.allowed.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<[u8; COUNT]> for AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &[u8; COUNT]) -> bool {
- *self.allowed.as_slice() == *other.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<AllowedAscii<COUNT>> for [u8; COUNT] {
- #[inline]
- fn eq(&self, other: &AllowedAscii<COUNT>) -> bool {
- *self.as_slice() == *other.allowed.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<[u8; COUNT]> for &AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &[u8; COUNT]) -> bool {
- *self.allowed.as_slice() == *other.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<&AllowedAscii<COUNT>> for [u8; COUNT] {
- #[inline]
- fn eq(&self, other: &&AllowedAscii<COUNT>) -> bool {
- *self.as_slice() == *other.allowed.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<&[u8; COUNT]> for AllowedAscii<COUNT> {
- #[inline]
- fn eq(&self, other: &&[u8; COUNT]) -> bool {
- *self.allowed.as_slice() == *other.as_slice()
- }
-}
-impl<const COUNT: usize> PartialEq<AllowedAscii<COUNT>> for &[u8; COUNT] {
- #[inline]
- fn eq(&self, other: &AllowedAscii<COUNT>) -> bool {
- *self.as_slice() == *other.allowed.as_slice()
- }
-}
/// Printable ASCII that should not need to be "escaped". That is to say
/// printable ASCII excluding space (i.e., 32), dot (i.e. 46), and backslash (i.e., 92).
/// This returns all `u8`s inclusively between 33 and 126 except 46 and 92.
-pub const PRINTABLE_ASCII: AllowedAscii<92> = AllowedAscii {
+pub const PRINTABLE_ASCII: AllowedAscii<[u8; 92]> = AllowedAscii {
allowed: [
b'!', b'"', b'#', b'$', b'%', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'/', b'0',
b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
@@ -285,7 +197,7 @@ pub const PRINTABLE_ASCII: AllowedAscii<92> = AllowedAscii {
/// This contains the following `u8`s:
///
/// 33, 35–39, 42–43, 45, 47–57, 61, 63, 65–90, and 94–126.
-pub const RFC5322_ATEXT: AllowedAscii<81> = AllowedAscii {
+pub const RFC5322_ATEXT: AllowedAscii<[u8; 81]> = AllowedAscii {
allowed: [
b'!', b'#', b'$', b'%', b'&', b'\'', b'*', b'+', b'-', b'/', b'0', b'1', b'2', b'3', b'4',
b'5', b'6', b'7', b'8', b'9', b'=', b'?', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H',
@@ -300,7 +212,7 @@ pub const RFC5322_ATEXT: AllowedAscii<81> = AllowedAscii {
/// This contains the following `u8`s:
///
/// 33, 36, 38–41, 43–45, 48–57, 59, 61, 65–90, 95–123, and 125–126.
-pub const ASCII_FIREFOX: AllowedAscii<78> = AllowedAscii {
+pub const ASCII_FIREFOX: AllowedAscii<[u8; 78]> = AllowedAscii {
allowed: [
b'!', b'$', b'&', b'\'', b'(', b')', b'+', b',', b'-', b'0', b'1', b'2', b'3', b'4', b'5',
b'6', b'7', b'8', b'9', b';', b'=', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I',
@@ -312,7 +224,7 @@ pub const ASCII_FIREFOX: AllowedAscii<78> = AllowedAscii {
};
/// ASCII hyphen, digits, and letters.
/// This contains 45 and all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122.
-pub const ASCII_HYPHEN_DIGITS_LETTERS: AllowedAscii<63> = AllowedAscii {
+pub const ASCII_HYPHEN_DIGITS_LETTERS: AllowedAscii<[u8; 63]> = AllowedAscii {
allowed: [
b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D',
b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S',
@@ -323,7 +235,7 @@ pub const ASCII_HYPHEN_DIGITS_LETTERS: AllowedAscii<63> = AllowedAscii {
};
/// ASCII digits and letters.
/// This contains all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122.
-pub const ASCII_DIGITS_LETTERS: AllowedAscii<62> = AllowedAscii {
+pub const ASCII_DIGITS_LETTERS: AllowedAscii<[u8; 62]> = AllowedAscii {
allowed: [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E',
b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T',
@@ -334,7 +246,7 @@ pub const ASCII_DIGITS_LETTERS: AllowedAscii<62> = AllowedAscii {
};
/// ASCII letters.
/// This contains all `u8`s inclusively between 65 and 90 and 97 and 122.
-pub const ASCII_LETTERS: AllowedAscii<52> = AllowedAscii {
+pub const ASCII_LETTERS: AllowedAscii<[u8; 52]> = AllowedAscii {
allowed: [
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd',
@@ -344,7 +256,7 @@ pub const ASCII_LETTERS: AllowedAscii<52> = AllowedAscii {
};
/// ASCII hyphen, digits, and uppercase letters.
/// This contains 45 and all `u8`s inclusively between 48 and 57 and 65 and 90.
-pub const ASCII_HYPHEN_DIGITS_UPPERCASE: AllowedAscii<37> = AllowedAscii {
+pub const ASCII_HYPHEN_DIGITS_UPPERCASE: AllowedAscii<[u8; 37]> = AllowedAscii {
allowed: [
b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D',
b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S',
@@ -353,7 +265,7 @@ pub const ASCII_HYPHEN_DIGITS_UPPERCASE: AllowedAscii<37> = AllowedAscii {
};
/// ASCII hyphen, digits, and lowercase letters.
/// This contains 45 and all `u8`s inclusively between 48 and 57 and 97 and 122.
-pub const ASCII_HYPHEN_DIGITS_LOWERCASE: AllowedAscii<37> = AllowedAscii {
+pub const ASCII_HYPHEN_DIGITS_LOWERCASE: AllowedAscii<[u8; 37]> = AllowedAscii {
allowed: [
b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd',
b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's',
@@ -362,7 +274,7 @@ pub const ASCII_HYPHEN_DIGITS_LOWERCASE: AllowedAscii<37> = AllowedAscii {
};
/// ASCII digits and uppercase letters.
/// This contains all `u8`s inclusively between 48 and 57 and 65 and 90.
-pub const ASCII_DIGITS_UPPERCASE: AllowedAscii<36> = AllowedAscii {
+pub const ASCII_DIGITS_UPPERCASE: AllowedAscii<[u8; 36]> = AllowedAscii {
allowed: [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E',
b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T',
@@ -371,7 +283,7 @@ pub const ASCII_DIGITS_UPPERCASE: AllowedAscii<36> = AllowedAscii {
};
/// ASCII digits and lowercase letters.
/// This contains all `u8`s inclusively between 48 and 57 and 97 and 122.
-pub const ASCII_DIGITS_LOWERCASE: AllowedAscii<36> = AllowedAscii {
+pub const ASCII_DIGITS_LOWERCASE: AllowedAscii<[u8; 36]> = AllowedAscii {
allowed: [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e',
b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't',
@@ -380,7 +292,7 @@ pub const ASCII_DIGITS_LOWERCASE: AllowedAscii<36> = AllowedAscii {
};
/// ASCII uppercase letters.
/// This contains all `u8`s inclusively between 65 and 90.
-pub const ASCII_UPPERCASE: AllowedAscii<26> = AllowedAscii {
+pub const ASCII_UPPERCASE: AllowedAscii<[u8; 26]> = AllowedAscii {
allowed: [
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z',
@@ -388,7 +300,7 @@ pub const ASCII_UPPERCASE: AllowedAscii<26> = AllowedAscii {
};
/// ASCII lowercase letters.
/// This contains all `u8`s inclusively between 97 and 122.
-pub const ASCII_LOWERCASE: AllowedAscii<26> = AllowedAscii {
+pub const ASCII_LOWERCASE: AllowedAscii<[u8; 26]> = AllowedAscii {
allowed: [
b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
@@ -396,7 +308,7 @@ pub const ASCII_LOWERCASE: AllowedAscii<26> = AllowedAscii {
};
/// ASCII digits.
/// This contains all `u8`s inclusively between 48 and 57.
-pub const ASCII_DIGITS: AllowedAscii<10> = AllowedAscii {
+pub const ASCII_DIGITS: AllowedAscii<[u8; 10]> = AllowedAscii {
allowed: [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9'],
};
#[cfg(test)]
@@ -410,15 +322,25 @@ mod tests {
#[test]
fn try_from() {
// Empty is allowed.
- assert!(AllowedAscii::try_from([]).is_ok());
+ assert!(AllowedAscii::try_from_unique_ascii([]).is_ok());
// Duplicates are not allowed.
- assert!(AllowedAscii::try_from(b"aba".to_owned())
+ assert!(AllowedAscii::try_from_unique_ascii(b"aba".to_owned())
.map_or_else(|e| e == AsciiErr::Duplicate(b'a'), |_| false));
// `b'.'` is not allowed.
- assert!(AllowedAscii::try_from(b"a.c".to_owned())
+ assert!(AllowedAscii::try_from_unique_ascii(b"a.c".to_owned())
.map_or_else(|e| e == AsciiErr::Contains46, |_| false));
+ // At most 127 bytes are allowed.
+ assert!(AllowedAscii::try_from_unique_ascii([0; 128])
+ .map_or_else(|e| e == AsciiErr::CountTooLarge(128), |_| false));
+ let mut all_ascii = (0..b'.').collect::<Vec<u8>>();
+ let next = b'.' + 1;
+ all_ascii.extend(next..=127);
+ assert!(AllowedAscii::try_from_unique_ascii(all_ascii).is_ok());
+ // Only ASCII is allowed.
+ assert!(AllowedAscii::try_from_unique_ascii([255])
+ .map_or_else(|e| e == AsciiErr::InvalidByte(255), |_| false));
assert!(
- AllowedAscii::try_from(b"abcdef".to_owned()).map_or(false, |bytes| bytes
+ AllowedAscii::try_from_unique_ascii(b"abcdef".to_owned()).map_or(false, |bytes| bytes
.contains(b'a')
&& bytes.contains(b'b')
&& bytes.contains(b'c')
diff --git a/src/dom.rs b/src/dom.rs
@@ -1,4 +1,4 @@
-use crate::char_set::{AllowedAscii, ASCII_HYPHEN_DIGITS_LETTERS};
+use crate::char_set::AllowedAscii;
use core::{
borrow::Borrow,
cmp::Ordering,
@@ -53,82 +53,10 @@ impl From<DomainOrdering> for Ordering {
}
}
}
-/// A flag used to indicate information about the characters in a [`Domain`]. This flag is used
-/// to perform more efficient comparisons that can potentially avoid temporary memory allocations
-/// to treat uppercase letters as if they were lowercase.
-///
-/// The reason `b'\\'`, `b']'`, `b'^'`, `b'_'`, and `` b'`' `` need to be tracked in addition to letters
-/// is to ensure uppercase letters are considered greater since lowercase letters are. As the documentation
-/// of `Domain` states, "uppercase letters are treated as lowercase".
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
-enum CharFlag {
- /// No ASCII letters, `b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``.
- None = 0,
- /// ASCII lowercase letters but no uppercase letters, `b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``.
- Lower = 1,
- /// ASCII uppercase letters but no lowercase letters, `b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``.
- Upper = 2,
- /// ASCII lowercase and uppercase letters but no `b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``.
- LowerUpper = 3,
- /// 'b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``; but no ASCII letters.
- Between = 4,
- /// ASCII lowercase letters and `b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``; but no uppercase letters.
- LowerBetween = 5,
- /// ASCII uppercase letters and `b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``; but no lowercase letters.
- UpperBetween = 6,
- /// ASCII lowercase letters, uppercase letters, and `b'\\'`, `b']'`, `b'^'`, `b'_'`, or `` b'`' ``.
- All = 7,
-}
-impl CharFlag {
- /// Returns a `bool` that indicates if equivalence must be done in a case insensitive way.
- #[inline]
- #[must_use]
- const fn eq_ignore_case(self, other: Self) -> bool {
- match self {
- Self::None | Self::Between => false,
- Self::Lower | Self::LowerBetween => !matches!(
- other,
- Self::None | Self::Between | Self::Lower | Self::LowerBetween
- ),
- Self::Upper | Self::UpperBetween => !matches!(
- other,
- Self::None | Self::Between | Self::Upper | Self::UpperBetween
- ),
- Self::LowerUpper | Self::All => !matches!(other, Self::None | Self::Between),
- }
- }
-}
-impl Display for CharFlag {
- #[inline]
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match *self {
- Self::None => f.write_str("No ASCII letters, '\\', ']', '^', '_', or '`'"),
- Self::Lower => f.write_str(
- "ASCII lowercase letters; but no uppercase letters, '\\', ']', '^', '_', or '`'",
- ),
- Self::Upper => f.write_str(
- "ASCII uppercase letters; but no lowercase letters, '\\', ']', '^', '_', or '`'",
- ),
- Self::Between => f.write_str("ASCII '\\', ']', '^', '_', or '`'; but no letters"),
- Self::LowerUpper => f.write_str(
- "ASCII lowercase and uppercase letters; but no '\\', ']', '^', '_', or '`'",
- ),
- Self::LowerBetween => f.write_str(
- "ASCII lowercase letters and '\\', ']', '^', '_', or '`'; but no uppercase letters",
- ),
- Self::UpperBetween => f.write_str(
- "ASCII uppercase letters and '\\', ']', '^', '_', or '`'; but no lowercase letters",
- ),
- Self::All => f.write_str(
- "ASCII lowercase letters, uppercase letters, and '\\', ']', '^', '_', or '`'",
- ),
- }
- }
-}
/// A domain that consists of at least one [`Label`]. With each `Label` only containing the ASCII `u8`s in
-/// [`Self::allowed_ascii`]. The total length of a `Domain` is at most 253 bytes[^note] in length including the
-/// `b'.'` separator. The trailing `b'.'`, if one exists, is always ignored.
+/// the [`AllowedAscii`] passed to [`Self::try_from_bytes`]. The total length of a `Domain` is at most
+/// 253 bytes[^note] in length including the `b'.'` separator. The trailing `b'.'`, if one exists, is always
+/// ignored.
///
/// This is more restrictive than what a domain is allowed to be per the
/// [Domain Name System (DNS)](https://www.rfc-editor.org/rfc/rfc2181) since all octets/`u8`s are allowed in a
@@ -144,22 +72,18 @@ impl Display for CharFlag {
/// 254. This is due to the fact that there is no way to explicitly represent the root label which in wire format
/// contributes one byte due to each label being preceded by the octet that represents its length.
#[derive(Clone, Debug)]
-pub struct Domain<'a, const ALLOWED_COUNT: usize, T> {
+pub struct Domain<T> {
/// The domain value. `value.as_ref().len()` is guaranteed to have length between 1 and 253 when the last `u8`
/// is not `b'.'`; otherwise the length is between 2 and 254.
/// Guaranteed to only contain `b'.'` and the ASCII `u8`s in `allowed_ascii`.
value: T,
- /// The allowed ASCII `u8`s a `Label` can have.
- allowed_ascii: &'a AllowedAscii<ALLOWED_COUNT>,
/// The lengths of each label. Guaranteed to have length between 1 and 127 with each value being
/// between 1 and 63.
/// The sum of each value plus the length is guaranteed to be 1 greater than `value.as_ref().len()` when
/// the last `u8` in `value` is not `b'.'`; otherwise it will be the same.
label_lens: Vec<NonZeroU8>,
- /// Flag that contains information about the kind of ASCII `u8`s in `value`.
- flag: CharFlag,
}
-impl<'a, 'b, const ALLOWED_COUNT: usize> Domain<'a, ALLOWED_COUNT, &'b [u8]> {
+impl<'a> Domain<&'a [u8]> {
/// Same as [`Self::as_bytes`] except the lifetime is tied to the slice and not `self`.
///
/// # Example
@@ -171,13 +95,13 @@ impl<'a, 'b, const ALLOWED_COUNT: usize> Domain<'a, ALLOWED_COUNT, &'b [u8]> {
#[allow(clippy::as_conversions, clippy::indexing_slicing)]
#[inline]
#[must_use]
- pub fn domain_without_trailing_dot(&self) -> &'b [u8] {
+ pub fn domain_without_trailing_dot(&self) -> &'a [u8] {
// `self.len().get() as usize` is fine since it's a positive `u8`.
// Indexing won't `panic` since `self.len()` is at most as long as `self.value`.
&self.value[..self.len().get() as usize]
}
}
-impl<'a, 'b, const ALLOWED_COUNT: usize> Domain<'a, ALLOWED_COUNT, &'b str> {
+impl<'a> Domain<&'a str> {
/// Same as [`Self::as_str`] except the lifetime is tied to the `str` and not `self`.
///
/// # Example
@@ -189,7 +113,7 @@ impl<'a, 'b, const ALLOWED_COUNT: usize> Domain<'a, ALLOWED_COUNT, &'b str> {
#[allow(unsafe_code, clippy::as_conversions, clippy::indexing_slicing)]
#[inline]
#[must_use]
- pub fn domain_without_trailing_dot(&self) -> &'b str {
+ pub fn domain_without_trailing_dot(&self) -> &'a str {
// `self.len().get() as usize` is fine since it's a positive `u8`.
// Indexing won't `panic` since `self.len()` is at most as long as `self.value`.
let utf8 = &self.value.as_bytes()[..self.len().get() as usize];
@@ -198,67 +122,51 @@ impl<'a, 'b, const ALLOWED_COUNT: usize> Domain<'a, ALLOWED_COUNT, &'b str> {
unsafe { str::from_utf8_unchecked(utf8) }
}
}
-impl<'a: 'b, 'b, const ALLOWED_COUNT: usize> From<Domain<'a, ALLOWED_COUNT, Vec<u8>>>
- for Domain<'b, ALLOWED_COUNT, String>
-{
+impl From<Domain<Vec<u8>>> for Domain<String> {
#[allow(unsafe_code)]
#[inline]
- fn from(value: Domain<'a, ALLOWED_COUNT, Vec<u8>>) -> Self {
+ fn from(value: Domain<Vec<u8>>) -> Self {
// SAFETY:
// We only allow ASCII, so this is fine.
let val = unsafe { String::from_utf8_unchecked(value.value) };
Self {
value: val,
- allowed_ascii: value.allowed_ascii,
label_lens: value.label_lens,
- flag: value.flag,
}
}
}
-impl<'a: 'b, 'b, const ALLOWED_COUNT: usize> From<Domain<'a, ALLOWED_COUNT, String>>
- for Domain<'b, ALLOWED_COUNT, Vec<u8>>
-{
+impl From<Domain<String>> for Domain<Vec<u8>> {
#[inline]
- fn from(value: Domain<'a, ALLOWED_COUNT, String>) -> Self {
+ fn from(value: Domain<String>) -> Self {
Self {
value: value.value.into_bytes(),
- allowed_ascii: value.allowed_ascii,
label_lens: value.label_lens,
- flag: value.flag,
}
}
}
-impl<'a: 'b, 'b, 'c: 'd, 'd, const ALLOWED_COUNT: usize> From<Domain<'a, ALLOWED_COUNT, &'c [u8]>>
- for Domain<'b, ALLOWED_COUNT, &'d str>
-{
+impl<'a: 'b, 'b> From<Domain<&'a [u8]>> for Domain<&'b str> {
#[allow(unsafe_code)]
#[inline]
- fn from(value: Domain<'a, ALLOWED_COUNT, &'c [u8]>) -> Self {
+ fn from(value: Domain<&'a [u8]>) -> Self {
// SAFETY:
// We only allow ASCII, so this is fine.
let val = unsafe { str::from_utf8_unchecked(value.value) };
Self {
value: val,
- allowed_ascii: value.allowed_ascii,
label_lens: value.label_lens,
- flag: value.flag,
}
}
}
-impl<'a: 'b, 'b, 'c: 'd, 'd, const ALLOWED_COUNT: usize> From<Domain<'a, ALLOWED_COUNT, &'c str>>
- for Domain<'b, ALLOWED_COUNT, &'d [u8]>
-{
+impl<'a: 'b, 'b> From<Domain<&'a str>> for Domain<&'b [u8]> {
#[inline]
- fn from(value: Domain<'a, ALLOWED_COUNT, &'c str>) -> Self {
+ fn from(value: Domain<&'a str>) -> Self {
Self {
value: value.value.as_bytes(),
- allowed_ascii: value.allowed_ascii,
label_lens: value.label_lens,
- flag: value.flag,
}
}
}
-impl<'a, const ALLOWED_COUNT: usize, T> Domain<'a, ALLOWED_COUNT, T> {
+impl<T> Domain<T> {
/// The maximum length of a `Domain` which is 253.
// SAFETY: 0 < 253 < 256.
#[allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
@@ -309,20 +217,8 @@ impl<'a, const ALLOWED_COUNT: usize, T> Domain<'a, ALLOWED_COUNT, T> {
pub fn into_inner(self) -> T {
self.value
}
- /// Returns the ASCII `u8`s that are allowed in a [`Label`].
- ///
- /// # Example
- ///
- /// ```
- /// use ascii_domain::{dom::Domain, char_set::ASCII_LOWERCASE};
- /// assert!(Domain::try_from_bytes("example.com", &ASCII_LOWERCASE).unwrap().allowed_ascii() == ASCII_LOWERCASE);
- /// ```
- #[inline]
- pub const fn allowed_ascii(&self) -> &'a AllowedAscii<ALLOWED_COUNT> {
- self.allowed_ascii
- }
}
-impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T> {
+impl<T: AsRef<[u8]>> Domain<T> {
/// Returns `true` iff the domain contains a trailing `b'.'`.
///
/// # Example
@@ -420,13 +316,12 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
clippy::arithmetic_side_effects,
clippy::as_conversions,
clippy::indexing_slicing,
- clippy::into_iter_on_ref,
- clippy::unreachable
+ clippy::into_iter_on_ref
)]
#[inline]
- pub fn try_from_bytes<'b: 'a>(
+ pub fn try_from_bytes<T2: AsRef<[u8]>>(
v: T,
- allowed_ascii: &'b AllowedAscii<ALLOWED_COUNT>,
+ allowed_ascii: &AllowedAscii<T2>,
) -> Result<Self, DomainErr> {
let val = v.as_ref();
let value = match val.last() {
@@ -451,32 +346,19 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
} else {
let mut label_lens = Vec::with_capacity(3);
let mut label_len = 0;
- // This flag is the `u8` representation of `CharFlag`.
- let mut flag = 0u8;
value
.into_iter()
.try_fold((), |(), byt| {
let b = *byt;
if b == b'.' {
- return NonZeroU8::new(label_len).map_or(
- Err(DomainErr::EmptyLabel),
- |length| {
- label_lens.push(length);
- label_len = 0;
- Ok(())
- },
- );
- } else if allowed_ascii.contains(b) {
- match b {
- b'A'..=b'Z' => flag |= 2,
- b'['..=b'`' => flag |= 4,
- b'a'..=b'z' => flag |= 1,
- _ => (),
- }
- } else {
- return Err(DomainErr::InvalidByte(b));
- }
- if label_len == 63 {
+ NonZeroU8::new(label_len).map_or(Err(DomainErr::EmptyLabel), |length| {
+ label_lens.push(length);
+ label_len = 0;
+ Ok(())
+ })
+ } else if !allowed_ascii.contains(b) {
+ Err(DomainErr::InvalidByte(b))
+ } else if label_len == 63 {
Err(DomainErr::LabelLenExceeds63)
} else {
// This is less than 63 due to the above check, so this won't overflow;
@@ -491,19 +373,7 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
label_lens.push(length);
Self {
value: v,
- allowed_ascii,
label_lens,
- flag: match flag {
- 0 => CharFlag::None,
- 1 => CharFlag::Lower,
- 2 => CharFlag::Upper,
- 3 => CharFlag::LowerUpper,
- 4 => CharFlag::Between,
- 5 => CharFlag::LowerBetween,
- 6 => CharFlag::UpperBetween,
- 7 => CharFlag::All,
- _ => unreachable!("there is a bug in Domain::try_from_bytes"),
- },
}
})
})
@@ -517,7 +387,7 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
/// assert!(Domain::try_from_bytes("example.com", &ASCII_LOWERCASE).unwrap().iter().next().unwrap().as_str() == "com");
/// ```
#[inline]
- pub fn iter(&self) -> LabelIter<'_, '_, ALLOWED_COUNT, T> {
+ pub fn iter(&self) -> LabelIter<T> {
LabelIter::new(self)
}
/// Returns `true` iff `self` and `right` are part of the same branch in the DNS hierarchy.
@@ -538,41 +408,17 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
/// assert!(!dom1.same_branch(&dom3));
/// ```
#[inline]
- pub fn same_branch<const ALLOWED_COUNT2: usize, T2: AsRef<[u8]>>(
- &self,
- right: &Domain<ALLOWED_COUNT2, T2>,
- ) -> bool {
- /// Compares `left` and `right` ignoring ASCII case such that `Ok` is returned iff `true`
- /// and `Err` is returned iff `false`.
- #[inline]
- fn eq_ignore(left: &str, right: &str) -> Result<(), ()> {
- if left.eq_ignore_ascii_case(right) {
- Ok(())
- } else {
- Err(())
- }
- }
- /// Compares `left` and `right` such that `Ok` is returned iff `true` and `Err` is returned iff `false`.
- #[inline]
- fn eq(left: &str, right: &str) -> Result<(), ()> {
- if left == right {
- Ok(())
- } else {
- Err(())
- }
- }
+ pub fn same_branch<T2: AsRef<[u8]>>(&self, right: &Domain<T2>) -> bool {
// Faster to check the values as bytes and not iterate each `Label`.
if self == right {
true
} else {
- let f = if self.flag.eq_ignore_case(right.flag) {
- eq_ignore
- } else {
- eq
- };
self.into_iter()
.zip(right)
- .try_fold((), |(), (label, label2)| f(label.value, label2.value))
+ .try_fold(
+ (),
+ |(), (label, label2)| if label == label2 { Ok(()) } else { Err(()) },
+ )
.map_or(false, |()| true)
}
}
@@ -601,151 +447,30 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
/// assert!(matches!(dom1.cmp_by_domain_ordering(&dom3), DomainOrdering::Less));
/// assert!(matches!(dom3.cmp_by_domain_ordering(&dom1), DomainOrdering::Greater));
/// ```
- #[allow(clippy::too_many_lines, clippy::unreachable)]
+ #[allow(clippy::unreachable)]
#[inline]
- pub fn cmp_by_domain_ordering<const ALLOWED_COUNT2: usize, T2: AsRef<[u8]>>(
- &self,
- right: &Domain<ALLOWED_COUNT2, T2>,
- ) -> DomainOrdering {
- // Faster to compare the entire value when we can.
+ pub fn cmp_by_domain_ordering<T2: AsRef<[u8]>>(&self, right: &Domain<T2>) -> DomainOrdering {
+ // Faster to compare the entire value when we can instead each `Label`.
if self == right {
- return DomainOrdering::Equal;
- }
- let left_input;
- let right_input;
- let left_dom;
- let right_dom;
- // We try to avoid needless converting to lowercase or uppercase.
- // Note that `CharFlag` does not need to be perfect. It only needs to be correct "enough" for comparisons.
- // For example, if we convert a `Domain` that contains uppercase letters to lowercase; and we compare
- // said `Domain` to one that only contains lowercase letters, `CharFlag::Lower`, `CharFlag::Between`,
- // and `CharFlag::LowerBetween` all will lead to a correct comparison.
- let (left_ref, right_ref) = match (self.flag, right.flag) {
- (CharFlag::None, _)
- | (_, CharFlag::None)
- | (
- CharFlag::Lower | CharFlag::Between | CharFlag::LowerBetween,
- CharFlag::Lower | CharFlag::Between | CharFlag::LowerBetween,
- )
- | (CharFlag::Upper, CharFlag::Upper) => {
- left_dom = Domain {
- value: self.value.as_ref(),
- allowed_ascii: self.allowed_ascii,
- label_lens: self.label_lens.clone(),
- flag: self.flag,
- };
- right_dom = Domain {
- value: right.value.as_ref(),
- allowed_ascii: right.allowed_ascii,
- label_lens: right.label_lens.clone(),
- flag: right.flag,
- };
- (&left_dom, &right_dom)
- }
- (CharFlag::Lower | CharFlag::LowerBetween | CharFlag::Between, _) => {
- left_dom = Domain {
- value: self.value.as_ref(),
- allowed_ascii: self.allowed_ascii,
- label_lens: self.label_lens.clone(),
- flag: self.flag,
- };
- right_input = right.value.as_ref().to_ascii_lowercase();
- right_dom = Domain {
- value: right_input.as_ref(),
- allowed_ascii: right.allowed_ascii,
- label_lens: right.label_lens.clone(),
- flag: CharFlag::LowerBetween,
- };
- (&left_dom, &right_dom)
- }
- (CharFlag::Upper, CharFlag::LowerUpper) => {
- left_dom = Domain {
- value: self.value.as_ref(),
- allowed_ascii: self.allowed_ascii,
- label_lens: self.label_lens.clone(),
- flag: self.flag,
- };
- right_input = right.value.as_ref().to_ascii_uppercase();
- right_dom = Domain {
- value: right_input.as_ref(),
- allowed_ascii: right.allowed_ascii,
- label_lens: right.label_lens.clone(),
- flag: CharFlag::Upper,
- };
- (&left_dom, &right_dom)
- }
- (_, CharFlag::Lower | CharFlag::Between | CharFlag::LowerBetween) => {
- left_input = self.value.as_ref().to_ascii_lowercase();
- left_dom = Domain {
- value: left_input.as_ref(),
- allowed_ascii: self.allowed_ascii,
- label_lens: self.label_lens.clone(),
- flag: CharFlag::LowerBetween,
- };
- right_dom = Domain {
- value: right.value.as_ref(),
- allowed_ascii: right.allowed_ascii,
- label_lens: right.label_lens.clone(),
- flag: right.flag,
- };
- (&left_dom, &right_dom)
- }
- (CharFlag::LowerUpper, CharFlag::Upper) => {
- left_input = self.value.as_ref().to_ascii_uppercase();
- left_dom = Domain {
- value: left_input.as_ref(),
- allowed_ascii: self.allowed_ascii,
- label_lens: self.label_lens.clone(),
- flag: CharFlag::Upper,
- };
- right_dom = Domain {
- value: right.value.as_ref(),
- allowed_ascii: right.allowed_ascii,
- label_lens: right.label_lens.clone(),
- flag: right.flag,
- };
- (&left_dom, &right_dom)
- }
- (_, _) => {
- left_input = self.value.as_ref().to_ascii_lowercase();
- left_dom = Domain {
- value: left_input.as_ref(),
- allowed_ascii: self.allowed_ascii,
- label_lens: self.label_lens.clone(),
- flag: CharFlag::LowerBetween,
- };
- right_input = right.value.as_ref().to_ascii_lowercase();
- right_dom = Domain {
- value: right_input.as_ref(),
- allowed_ascii: right.allowed_ascii,
- label_lens: right.label_lens.clone(),
- flag: CharFlag::LowerBetween,
- };
- (&left_dom, &right_dom)
- }
- };
- left_ref
- .into_iter()
- .zip(right_ref)
- .try_fold((), |(), (label, label2)| {
- // We don't want to use `Label::cmp` since that always converts both `Label`s to
- // lowercase since it is unaware of `CharFlag`s. This is fine due to above where
- // we did the conversion already.
- match label.value.cmp(label2.value) {
+ DomainOrdering::Equal
+ } else {
+ self.into_iter()
+ .zip(right)
+ .try_fold((), |(), (label, label2)| match label.cmp(&label2) {
Ordering::Less => Err(DomainOrdering::Less),
Ordering::Equal => Ok(()),
Ordering::Greater => Err(DomainOrdering::Greater),
- }
- })
- .map_or_else(convert::identity, |()| {
- match self.label_count().cmp(&right.label_count()) {
- Ordering::Less => DomainOrdering::Shorter,
- Ordering::Equal => {
- unreachable!("there is a bug in Domain::cmp_by_domain_ordering")
+ })
+ .map_or_else(convert::identity, |()| {
+ match self.label_count().cmp(&right.label_count()) {
+ Ordering::Less => DomainOrdering::Shorter,
+ Ordering::Equal => {
+ unreachable!("there is a bug in Domain::cmp_by_domain_ordering")
+ }
+ Ordering::Greater => DomainOrdering::Longer,
}
- Ordering::Greater => DomainOrdering::Longer,
- }
- })
+ })
+ }
}
/// The total order that is defined follows the following hierarchy:
/// 1. Pairwise comparisons of each [`Label`] starting from the TLDs.
@@ -773,10 +498,7 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
/// assert!(matches!(dom3.cmp_doms(&dom1), Ordering::Greater));
/// ```
#[inline]
- pub fn cmp_doms<const ALLOWED_COUNT2: usize, T2: AsRef<[u8]>>(
- &self,
- right: &Domain<ALLOWED_COUNT2, T2>,
- ) -> Ordering {
+ pub fn cmp_doms<T2: AsRef<[u8]>>(&self, right: &Domain<T2>) -> Ordering {
self.cmp_by_domain_ordering(right).into()
}
/// Returns the last `Label` (i.e., the TLD).
@@ -795,118 +517,73 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Domain<'a, ALLOWED_COUNT, T
.unwrap_or_else(|| unreachable!("there is a bug in Domain::try_from_bytes"))
}
}
-impl<
- 'a,
- 'b,
- const ALLOWED_COUNT: usize,
- const ALLOWED_COUNT2: usize,
- T: AsRef<[u8]>,
- T2: AsRef<[u8]>,
- > PartialEq<Domain<'a, ALLOWED_COUNT, T>> for Domain<'b, ALLOWED_COUNT2, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Domain<T>> for Domain<T2> {
/// Ignores the provided [`AllowedAscii`] and simply compares the two `Domain`s as [`Label`]s
/// of bytes. Note uppercase ASCII is treated as lowercase ASCII and trailing `b'.'`s are ignored.
#[inline]
- fn eq(&self, other: &Domain<ALLOWED_COUNT, T>) -> bool {
- if self.flag.eq_ignore_case(other.flag) {
- self.as_bytes().eq_ignore_ascii_case(other.as_bytes())
- } else {
- self.as_bytes() == other.as_bytes()
- }
+ fn eq(&self, other: &Domain<T>) -> bool {
+ self.as_bytes().eq_ignore_ascii_case(other.as_bytes())
}
}
-impl<
- 'a,
- 'b,
- const ALLOWED_COUNT: usize,
- const ALLOWED_COUNT2: usize,
- T: AsRef<[u8]>,
- T2: AsRef<[u8]>,
- > PartialEq<&Domain<'a, ALLOWED_COUNT, T>> for Domain<'b, ALLOWED_COUNT2, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<&Domain<T>> for Domain<T2> {
#[inline]
- fn eq(&self, other: &&Domain<'a, ALLOWED_COUNT, T>) -> bool {
+ fn eq(&self, other: &&Domain<T>) -> bool {
*self == **other
}
}
-impl<
- 'a,
- 'b,
- const ALLOWED_COUNT: usize,
- const ALLOWED_COUNT2: usize,
- T: AsRef<[u8]>,
- T2: AsRef<[u8]>,
- > PartialEq<Domain<'a, ALLOWED_COUNT, T>> for &Domain<'b, ALLOWED_COUNT2, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Domain<T>> for &Domain<T2> {
#[inline]
- fn eq(&self, other: &Domain<'a, ALLOWED_COUNT, T>) -> bool {
+ fn eq(&self, other: &Domain<T>) -> bool {
**self == *other
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Eq for Domain<'_, ALLOWED_COUNT, T> {}
-impl<
- 'a,
- 'b,
- const ALLOWED_COUNT: usize,
- const ALLOWED_COUNT2: usize,
- T: AsRef<[u8]>,
- T2: AsRef<[u8]>,
- > PartialOrd<Domain<'a, ALLOWED_COUNT, T>> for Domain<'b, ALLOWED_COUNT2, T2>
-{
+impl<T: AsRef<[u8]>> Eq for Domain<T> {}
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialOrd<Domain<T>> for Domain<T2> {
/// Consult [`Self::cmp_doms`].
#[inline]
- fn partial_cmp(&self, other: &Domain<ALLOWED_COUNT, T>) -> Option<Ordering> {
+ fn partial_cmp(&self, other: &Domain<T>) -> Option<Ordering> {
Some(self.cmp_doms(other))
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Ord for Domain<'_, ALLOWED_COUNT, T> {
+impl<T: AsRef<[u8]>> Ord for Domain<T> {
/// Consult [`Self::cmp_doms`].
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.cmp_doms(other)
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Hash for Domain<'_, ALLOWED_COUNT, T> {
+impl<T: AsRef<[u8]>> Hash for Domain<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
- match self.flag {
- CharFlag::None | CharFlag::Lower | CharFlag::Between | CharFlag::LowerBetween => {
- self.as_bytes().hash(state);
- }
- CharFlag::Upper | CharFlag::LowerUpper | CharFlag::UpperBetween | CharFlag::All => {
- self.as_bytes().to_ascii_lowercase().hash(state);
- }
- }
+ self.as_bytes().to_ascii_lowercase().hash(state);
}
}
-impl<'a, 'b: 'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>>
- TryFrom<(T, &'b AllowedAscii<ALLOWED_COUNT>)> for Domain<'a, ALLOWED_COUNT, T>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> TryFrom<(T, &AllowedAscii<T2>)> for Domain<T> {
type Error = DomainErr;
#[inline]
- fn try_from(value: (T, &'b AllowedAscii<ALLOWED_COUNT>)) -> Result<Self, Self::Error> {
+ fn try_from(value: (T, &AllowedAscii<T2>)) -> Result<Self, Self::Error> {
Self::try_from_bytes(value.0, value.1)
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Display for Domain<'_, ALLOWED_COUNT, T> {
+impl<T: AsRef<[u8]>> Display for Domain<T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> AsRef<str> for Domain<'_, ALLOWED_COUNT, T> {
+impl<T: AsRef<[u8]>> AsRef<str> for Domain<T> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> AsRef<[u8]> for Domain<'_, ALLOWED_COUNT, T> {
+impl<T: AsRef<[u8]>> AsRef<[u8]> for Domain<T> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Deref for Domain<'_, ALLOWED_COUNT, T> {
+impl<T: AsRef<[u8]>> Deref for Domain<T> {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
@@ -1126,9 +803,9 @@ impl<'a> Deref for Label<'a> {
}
}
/// [`Iterator`] that iterates [`Label`]s from a borrowed [`Domain`] starting from the TLD down.
-pub struct LabelIter<'a, 'b: 'a, const ALLOWED_COUNT: usize, T> {
+pub struct LabelIter<'a, T> {
/// Domain that contains `Label`s to iterate.
- domain: &'a Domain<'b, ALLOWED_COUNT, T>,
+ domain: &'a Domain<T>,
/// Starts at domain.label_count().get() - 1 which is valid since domain.label_count().get() > 0.
/// idx is 255 when the iterator is exhausted.
/// Since idx is decremented each time and it starts at a value less than 254, this is a valid value to use
@@ -1146,11 +823,11 @@ pub struct LabelIter<'a, 'b: 'a, const ALLOWED_COUNT: usize, T> {
/// After a label is read, 1 must be added to account for '.'.
start_back: u8,
}
-impl<'a, 'b: 'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> LabelIter<'a, 'b, ALLOWED_COUNT, T> {
+impl<'a, T: AsRef<[u8]>> LabelIter<'a, T> {
/// Helper function to construct an instance.
#[allow(clippy::arithmetic_side_effects)]
#[inline]
- fn new(domain: &'a Domain<'b, ALLOWED_COUNT, T>) -> Self {
+ fn new(domain: &'a Domain<T>) -> Self {
Self {
// This won't underflow since `label_count` is at least 1.
idx: domain.label_count().get() - 1,
@@ -1161,9 +838,7 @@ impl<'a, 'b: 'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> LabelIter<'a, 'b, A
}
}
}
-impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Iterator
- for LabelIter<'a, '_, ALLOWED_COUNT, T>
-{
+impl<'a, T: AsRef<[u8]>> Iterator for LabelIter<'a, T> {
type Item = Label<'a>;
#[allow(
unsafe_code,
@@ -1216,13 +891,8 @@ impl<'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> Iterator
opt
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> FusedIterator
- for LabelIter<'_, '_, ALLOWED_COUNT, T>
-{
-}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> ExactSizeIterator
- for LabelIter<'_, '_, ALLOWED_COUNT, T>
-{
+impl<T: AsRef<[u8]>> FusedIterator for LabelIter<'_, T> {}
+impl<T: AsRef<[u8]>> ExactSizeIterator for LabelIter<'_, T> {
#[allow(clippy::arithmetic_side_effects, clippy::as_conversions)]
#[inline]
fn len(&self) -> usize {
@@ -1237,9 +907,7 @@ impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> ExactSizeIterator
}
}
}
-impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> DoubleEndedIterator
- for LabelIter<'_, '_, ALLOWED_COUNT, T>
-{
+impl<T: AsRef<[u8]>> DoubleEndedIterator for LabelIter<'_, T> {
#[allow(
unsafe_code,
clippy::arithmetic_side_effects,
@@ -1284,11 +952,9 @@ impl<const ALLOWED_COUNT: usize, T: AsRef<[u8]>> DoubleEndedIterator
})
}
}
-impl<'a, 'b: 'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> IntoIterator
- for &'a Domain<'b, ALLOWED_COUNT, T>
-{
+impl<'a, T: AsRef<[u8]>> IntoIterator for &'a Domain<T> {
type Item = Label<'a>;
- type IntoIter = LabelIter<'a, 'b, ALLOWED_COUNT, T>;
+ type IntoIter = LabelIter<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
LabelIter::new(self)
@@ -1298,10 +964,10 @@ impl<'a, 'b: 'a, const ALLOWED_COUNT: usize, T: AsRef<[u8]>> IntoIterator
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Rfc1123Err {
- /// [`Domain::allowed_ascii`] is not [`ASCII_HYPHEN_DIGITS_LETTERS`].
- InvalidAllowedAscii,
/// The [`Domain`] has a trailing `b'.'`.
ContainsTrailingDot,
+ /// The [`Domain`] contains ASCII not in [`crate::char_set::ASCII_HYPHEN_DIGITS_LETTERS`].
+ InvalidAscii,
/// A [`Label`] of [`Domain`] starts with an ASCII hyphen.
LabelStartsWithAHyphen,
/// A [`Label`] of [`Domain`] ends with an ASCII hyphen.
@@ -1311,10 +977,10 @@ impl Display for Rfc1123Err {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
- Self::InvalidAllowedAscii => {
- f.write_str("the allowed ASCII is not letters, digits, and hyphen")
- }
Self::ContainsTrailingDot => f.write_str("the domain contained a trailing dot"),
+ Self::InvalidAscii => {
+ f.write_str("the domain contained ASCII besides hyphen, digits, and letters")
+ }
Self::LabelStartsWithAHyphen => {
f.write_str("a label in the domain starts with a hyphen")
}
@@ -1370,11 +1036,11 @@ impl Error for Rfc1123Err {}
/// RFC 1123 as if it were written presciently does not make sense. For that reason the more relaxed interpretation
/// is rejected. Consequently we use the most relaxed interpretation.
#[derive(Clone, Debug)]
-pub struct Rfc1123Domain<'a, T> {
+pub struct Rfc1123Domain<T> {
/// The domain.
- dom: Domain<'a, 63, T>,
+ dom: Domain<T>,
}
-impl<T> Rfc1123Domain<'_, T> {
+impl<T> Rfc1123Domain<T> {
/// Returns the contained [`Domain`].
///
/// # Example
@@ -1386,11 +1052,11 @@ impl<T> Rfc1123Domain<'_, T> {
/// assert!(Rfc1123Domain::try_from(dom).unwrap().domain() == dom2);
/// ```
#[inline]
- pub const fn domain(&self) -> &Domain<'_, 63, T> {
+ pub const fn domain(&self) -> &Domain<T> {
&self.dom
}
}
-impl<T: AsRef<[u8]>> Rfc1123Domain<'_, T> {
+impl<T: AsRef<[u8]>> Rfc1123Domain<T> {
/// Returns `true` iff the domain adheres to the literal interpretation of RFC 1123. For more information
/// read the description of [`Rfc1123Domain`].
///
@@ -1440,8 +1106,7 @@ impl<T: AsRef<[u8]>> Rfc1123Domain<'_, T> {
#[inline]
pub fn is_ipv4(&self) -> bool {
// Faster to check metadata first to hopefully avoid re-parsing the domain as an IPv4 address.
- self.dom.flag == CharFlag::None
- && self.as_bytes().len() < 16
+ self.as_bytes().len() < 16
&& self.label_count().get() == 4
// We don't use `std::net::Ipv4Addr::from_str` since that does not consider octets with leading
// 0s as valid. This means something like `0.0.0.01` is not considered an IPv4 address, but we
@@ -1474,157 +1139,135 @@ impl<T: AsRef<[u8]>> Rfc1123Domain<'_, T> {
.is_ok()
}
}
-impl<'a, 'b, T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Rfc1123Domain<'a, T>>
- for Rfc1123Domain<'b, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Rfc1123Domain<T>> for Rfc1123Domain<T2> {
#[inline]
- fn eq(&self, other: &Rfc1123Domain<'a, T>) -> bool {
+ fn eq(&self, other: &Rfc1123Domain<T>) -> bool {
self.dom == other.dom
}
}
-impl<'a, 'b, T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<&Rfc1123Domain<'a, T>>
- for Rfc1123Domain<'b, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<&Rfc1123Domain<T>> for Rfc1123Domain<T2> {
#[inline]
- fn eq(&self, other: &&Rfc1123Domain<'a, T>) -> bool {
+ fn eq(&self, other: &&Rfc1123Domain<T>) -> bool {
self.dom == other.dom
}
}
-impl<'a, 'b, T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Rfc1123Domain<'a, T>>
- for &Rfc1123Domain<'b, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Rfc1123Domain<T>> for &Rfc1123Domain<T2> {
#[inline]
- fn eq(&self, other: &Rfc1123Domain<'a, T>) -> bool {
+ fn eq(&self, other: &Rfc1123Domain<T>) -> bool {
self.dom == other.dom
}
}
-impl<'a, 'b, const ALLOWED_COUNT: usize, T: AsRef<[u8]>, T2: AsRef<[u8]>>
- PartialEq<Rfc1123Domain<'a, T>> for Domain<'b, ALLOWED_COUNT, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Rfc1123Domain<T>> for Domain<T2> {
#[inline]
- fn eq(&self, other: &Rfc1123Domain<'a, T>) -> bool {
+ fn eq(&self, other: &Rfc1123Domain<T>) -> bool {
*self == other.dom
}
}
-impl<'a, 'b, const ALLOWED_COUNT: usize, T: AsRef<[u8]>, T2: AsRef<[u8]>>
- PartialEq<Rfc1123Domain<'a, T>> for &Domain<'b, ALLOWED_COUNT, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Rfc1123Domain<T>> for &Domain<T2> {
#[inline]
- fn eq(&self, other: &Rfc1123Domain<'a, T>) -> bool {
+ fn eq(&self, other: &Rfc1123Domain<T>) -> bool {
**self == other.dom
}
}
-impl<'a, 'b, const ALLOWED_COUNT: usize, T: AsRef<[u8]>, T2: AsRef<[u8]>>
- PartialEq<&Rfc1123Domain<'a, T>> for Domain<'b, ALLOWED_COUNT, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<&Rfc1123Domain<T>> for Domain<T2> {
#[inline]
- fn eq(&self, other: &&Rfc1123Domain<'a, T>) -> bool {
+ fn eq(&self, other: &&Rfc1123Domain<T>) -> bool {
*self == other.dom
}
}
-impl<'a, 'b, const ALLOWED_COUNT: usize, T: AsRef<[u8]>, T2: AsRef<[u8]>>
- PartialEq<Domain<'a, ALLOWED_COUNT, T>> for Rfc1123Domain<'b, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialEq<Domain<T>> for Rfc1123Domain<T2> {
#[inline]
- fn eq(&self, other: &Domain<'a, ALLOWED_COUNT, T>) -> bool {
+ fn eq(&self, other: &Domain<T>) -> bool {
self.dom == *other
}
}
-impl<T: AsRef<[u8]>> Eq for Rfc1123Domain<'_, T> {}
-impl<'a, 'b, T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialOrd<Rfc1123Domain<'a, T>>
- for Rfc1123Domain<'b, T2>
-{
+impl<T: AsRef<[u8]>> Eq for Rfc1123Domain<T> {}
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialOrd<Rfc1123Domain<T>> for Rfc1123Domain<T2> {
#[inline]
- fn partial_cmp(&self, other: &Rfc1123Domain<'a, T>) -> Option<Ordering> {
+ fn partial_cmp(&self, other: &Rfc1123Domain<T>) -> Option<Ordering> {
self.dom.partial_cmp(&other.dom)
}
}
-impl<'a, 'b, const ALLOWED_COUNT: usize, T: AsRef<[u8]>, T2: AsRef<[u8]>>
- PartialOrd<Rfc1123Domain<'a, T>> for Domain<'b, ALLOWED_COUNT, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialOrd<Rfc1123Domain<T>> for Domain<T2> {
#[inline]
- fn partial_cmp(&self, other: &Rfc1123Domain<'a, T>) -> Option<Ordering> {
+ fn partial_cmp(&self, other: &Rfc1123Domain<T>) -> Option<Ordering> {
self.partial_cmp(&other.dom)
}
}
-impl<'a, 'b, const ALLOWED_COUNT: usize, T: AsRef<[u8]>, T2: AsRef<[u8]>>
- PartialOrd<Domain<'a, ALLOWED_COUNT, T>> for Rfc1123Domain<'b, T2>
-{
+impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialOrd<Domain<T>> for Rfc1123Domain<T2> {
#[inline]
- fn partial_cmp(&self, other: &Domain<'a, ALLOWED_COUNT, T>) -> Option<Ordering> {
+ fn partial_cmp(&self, other: &Domain<T>) -> Option<Ordering> {
self.dom.partial_cmp(other)
}
}
-impl<T: AsRef<[u8]>> Ord for Rfc1123Domain<'_, T> {
+impl<T: AsRef<[u8]>> Ord for Rfc1123Domain<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.dom.cmp(&other.dom)
}
}
-impl<T: AsRef<[u8]>> Hash for Rfc1123Domain<'_, T> {
+impl<T: AsRef<[u8]>> Hash for Rfc1123Domain<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.dom.hash(state);
}
}
-impl<'a, 'b: 'a, T> AsRef<Domain<'a, 63, T>> for Rfc1123Domain<'b, T> {
+impl<T> AsRef<Domain<T>> for Rfc1123Domain<T> {
#[inline]
- fn as_ref(&self) -> &Domain<'a, 63, T> {
+ fn as_ref(&self) -> &Domain<T> {
&self.dom
}
}
-impl<'a, 'b: 'a, T> Borrow<Domain<'a, 63, T>> for Rfc1123Domain<'b, T> {
+impl<T> Borrow<Domain<T>> for Rfc1123Domain<T> {
#[inline]
- fn borrow(&self) -> &Domain<'a, 63, T> {
+ fn borrow(&self) -> &Domain<T> {
&self.dom
}
}
-impl<'a, T> Deref for Rfc1123Domain<'a, T> {
- type Target = Domain<'a, 63, T>;
+impl<T> Deref for Rfc1123Domain<T> {
+ type Target = Domain<T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.dom
}
}
-impl<'a, 'b: 'a, T> From<Rfc1123Domain<'b, T>> for Domain<'a, 63, T> {
+impl<T> From<Rfc1123Domain<T>> for Domain<T> {
#[inline]
- fn from(value: Rfc1123Domain<'b, T>) -> Self {
+ fn from(value: Rfc1123Domain<T>) -> Self {
value.dom
}
}
-impl<'a, 'b: 'a, T: AsRef<[u8]>> TryFrom<Domain<'b, 63, T>> for Rfc1123Domain<'a, T> {
+impl<T: AsRef<[u8]>> TryFrom<Domain<T>> for Rfc1123Domain<T> {
type Error = Rfc1123Err;
- #[allow(
- clippy::arithmetic_side_effects,
- clippy::indexing_slicing,
- clippy::unreachable
- )]
+ #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)]
#[inline]
- fn try_from(value: Domain<'b, 63, T>) -> Result<Self, Self::Error> {
- if *value.allowed_ascii != ASCII_HYPHEN_DIGITS_LETTERS {
- Err(Rfc1123Err::InvalidAllowedAscii)
- } else if value.contains_trailing_dot() {
+ fn try_from(value: Domain<T>) -> Result<Self, Self::Error> {
+ if value.contains_trailing_dot() {
Err(Rfc1123Err::ContainsTrailingDot)
} else {
value
.into_iter()
.try_fold((), |(), label| {
- let bytes = label.as_bytes();
- // `Label`s are never empty, so the below indexing is fine.
- // Underflow won't occur for the same reason.
- if bytes[0] == b'-' {
- Err(Rfc1123Err::LabelStartsWithAHyphen)
- } else if bytes[bytes.len() - 1] == b'-' {
- Err(Rfc1123Err::LabelEndsWithAHyphen)
+ if label.is_hyphen_or_alphanumeric() {
+ let bytes = label.value.as_bytes();
+ // `Label`s are never empty, so the below indexing is fine.
+ // Underflow won't occur for the same reason.
+ if bytes[0] == b'-' {
+ Err(Rfc1123Err::LabelStartsWithAHyphen)
+ } else if bytes[bytes.len() - 1] == b'-' {
+ Err(Rfc1123Err::LabelEndsWithAHyphen)
+ } else {
+ Ok(())
+ }
} else {
- Ok(())
+ Err(Rfc1123Err::InvalidAscii)
}
})
.map(|()| Self { dom: value })
}
}
}
-impl<T: AsRef<[u8]>> Display for Rfc1123Domain<'_, T> {
+impl<T: AsRef<[u8]>> Display for Rfc1123Domain<T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.dom.fmt(f)
@@ -1633,7 +1276,7 @@ impl<T: AsRef<[u8]>> Display for Rfc1123Domain<'_, T> {
#[cfg(test)]
mod tests {
use super::{Domain, DomainErr, Rfc1123Domain, Rfc1123Err};
- use crate::char_set::{self, ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS};
+ use crate::char_set::{AllowedAscii, ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS};
use core::cmp::Ordering;
#[test]
fn test_dom_parse() {
@@ -1954,15 +1597,14 @@ mod tests {
.map_or_else(|e| e == Rfc1123Err::ContainsTrailingDot, |_| false)
)
);
- assert!(char_set::AllowedAscii::<63>::try_from(
- b"!@#$%^&*()1234567890asdfghjklqwertyuiopzxcvbnmASDFGHJKLZXCVBNMQ".to_owned()
- )
- .map_or(false, |ascii| {
- Domain::try_from_bytes("example.com", &ascii).map_or(false, |dom| {
- Rfc1123Domain::try_from(dom)
- .map_or_else(|e| e == Rfc1123Err::InvalidAllowedAscii, |_| false)
+ assert!(
+ AllowedAscii::try_from_unique_ascii(b"exampl!co".to_owned()).map_or(false, |ascii| {
+ Domain::try_from_bytes("exampl!e.com", &ascii).map_or(false, |dom| {
+ Rfc1123Domain::try_from(dom)
+ .map_or_else(|e| e == Rfc1123Err::InvalidAscii, |_| false)
+ })
})
- }));
+ );
assert!(
Domain::try_from_bytes("example-.com", &ASCII_HYPHEN_DIGITS_LETTERS).map_or(
false,