commit 06b89378239dbcc1eb0a2679bf8e62171e9d78e8
parent 80e6b40b45a7ae88e0360a888dc661d5aa504ab4
Author: Zack Newman <zack@philomathiclife.com>
Date: Tue, 30 Jan 2024 21:01:46 -0700
fix eq for domains
Diffstat:
3 files changed, 125 insertions(+), 98 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
name = "rpz"
readme = "README.md"
repository = "https://git.philomathiclife.com/repos/rpz/"
-version = "0.2.0"
+version = "0.2.1"
[lib]
name = "rpz"
@@ -21,12 +21,12 @@ path = "src/main.rs"
[dependencies]
num-bigint = { version = "0.4.4", default-features = false }
-reqwest = { version = "0.11.22", default-features = false, features = ["brotli", "deflate", "gzip", "rustls-tls-native-roots", "trust-dns"] }
-serde = { version = "1.0.192", default-features = false }
-superset_map = { version = "0.2.1", default-features = false }
-tokio = { version = "1.33.0", default-features = false, features = ["rt", "time"] }
+reqwest = { version = "0.11.23", default-features = false, features = ["brotli", "deflate", "gzip", "rustls-tls-native-roots", "trust-dns"] }
+serde = { version = "1.0.196", default-features = false }
+superset_map = { version = "0.2.2", default-features = false }
+tokio = { version = "1.35.1", default-features = false, features = ["rt", "time"] }
toml = { version = "0.8.8", default-features = false, features = ["parse"] }
-url = { version = "2.4.1", default-features = false, features = ["serde"] }
+url = { version = "2.5.0", default-features = false, features = ["serde"] }
zfc = { version = "0.3.1", default-features = false }
[target.'cfg(target_os = "openbsd")'.dependencies]
diff --git a/src/dom.rs b/src/dom.rs
@@ -16,13 +16,13 @@ use core::convert::{self, AsRef};
use core::fmt::{self, Display, Formatter};
use core::hash::{Hash, Hasher};
use core::iter::FusedIterator;
+use core::net::Ipv4Addr;
use core::num::NonZeroU8;
use core::ops::Deref;
use core::str;
use num_bigint::BigUint;
use std::error;
use std::io::{Error, Write};
-use std::net::Ipv4Addr;
use superset_map::SetOrd;
use zfc::{BoundedCardinality, Cardinality, Set};
/// A flag used to indicate information about the characters
@@ -35,15 +35,20 @@ use zfc::{BoundedCardinality, Cardinality, Set};
enum CharFlag {
/// No letters, ticks, or underscores.
None,
- /// No uppercase letters; but there are lowercase letters, ticks, or underscores.
- LowerOrTick,
- /// No lowercase letters, ticks, or underscores; but there are uppercase letters.
+ /// Lowercase letters but no uppercase letters, ticks, or underscores.
+ Lower,
+ /// Uppercase letters but no lowercase letters, ticks, or underscores.
Upper,
- /// There are uppercase letters and ticks or underscores.
- /// Note this means there can also be lowercase letters, but that is fine.
- UpperAndTick,
- /// There are both lowercase and uppercase letters, but there are no ticks or underscores.
- Mixed,
+ /// Ticks or underscores but no letters.
+ Ticks,
+ /// Uppercase and lowercase letters but no ticks or underscores.
+ LowerUpper,
+ /// Lowercase letters and ticks or underscores, but no uppercase letters.
+ LowerTicks,
+ /// Uppercase letters and ticks or underscores, but no lowercase letters.
+ UpperTicks,
+ /// Uppercase letters, lowercase letters, and ticks or underscores.
+ All,
}
impl CharFlag {
/// Returns a `bool` that indicates
@@ -52,12 +57,16 @@ impl CharFlag {
#[inline]
const fn eq_ignore_case(self, other: Self) -> bool {
match self {
- Self::None => false,
- Self::LowerOrTick => !matches!(other, Self::None | Self::LowerOrTick),
- Self::Upper | Self::UpperAndTick => {
- !matches!(other, Self::None | Self::Upper | Self::UpperAndTick)
- }
- Self::Mixed => !matches!(other, Self::None),
+ Self::None | Self::Ticks => false,
+ Self::Lower | Self::LowerTicks => !matches!(
+ other,
+ Self::None | Self::Ticks | Self::Lower | Self::LowerTicks
+ ),
+ Self::Upper | Self::UpperTicks => !matches!(
+ other,
+ Self::None | Self::Ticks | Self::Upper | Self::UpperTicks
+ ),
+ Self::LowerUpper | Self::All => !matches!(other, Self::None | Self::Ticks),
}
}
}
@@ -205,21 +214,18 @@ impl<'a> Domain<'a> {
)]
#[inline]
fn try_from_slice<'b: 'a>(mut value: &'b [u8]) -> Result<Self, DomainErr> {
- if Ipv4Addr::parse_ascii(value).is_ok() {
- return Err(DomainErr::Ipv4);
- }
- value = value.last().map_or(value, |byt| {
- if *byt == b'.' {
- &value[..value.len() - 1]
- } else {
- value
+ value = match value.last() {
+ None => return Err(DomainErr::Empty),
+ Some(byt) => {
+ if *byt == b'.' {
+ &value[..value.len() - 1]
+ } else {
+ value
+ }
}
- });
- let len = value.len();
- if len < Self::MIN_LEN.get() as usize {
- Err(DomainErr::Empty)
- } else if value.len() > Self::MAX_LEN.get() as usize {
- Err(DomainErr::LenExceeds253(len))
+ };
+ if value.len() > Self::MAX_LEN.get() as usize {
+ Err(DomainErr::LenExceeds253(value.len()))
} else {
let mut label_lens = Vec::with_capacity(3);
let mut label_len = 0;
@@ -229,61 +235,72 @@ impl<'a> Domain<'a> {
// 2 => uppercase letters; but no lowercase letters, ticks, or underscores.
// 4 => ticks or underscores, but no letters.
let mut flag = 0u8;
- match value.into_iter().try_fold((), |(), byt| {
- match *byt {
- b'.' => {
- return NonZeroU8::new(label_len).map_or(
- Err(DomainErr::EmptyLabel),
- |length| {
- label_lens.push(length);
- label_len = 0;
- Ok(())
- },
- )
+ value
+ .into_iter()
+ .try_fold((), |(), byt| {
+ match *byt {
+ b'.' => {
+ return NonZeroU8::new(label_len).map_or(
+ Err(DomainErr::EmptyLabel),
+ |length| {
+ label_lens.push(length);
+ label_len = 0;
+ Ok(())
+ },
+ )
+ }
+ b'A'..=b'Z' => flag |= 2,
+ b'`' | b'_' => flag |= 4,
+ b'a'..=b'z' => flag |= 1,
+ 0..=b' '
+ | b'"'..=b'#'
+ | b'%'
+ | b'*'
+ | b'/'
+ | b':'
+ | b'<'
+ | b'>'..=b'@'
+ | b'['..=b'^'
+ | b'|'
+ | 127.. => return Err(DomainErr::InvalidByte(*byt)),
+ _ => (),
}
- b'A'..=b'Z' => flag |= 2,
- b'`' | b'_' => flag |= 4,
- b'a'..=b'z' => flag |= 1,
- 0..=b' '
- | b'"'..=b'#'
- | b'%'
- | b'*'
- | b'/'
- | b':'
- | b'<'
- | b'>'..=b'@'
- | b'['..=b'^'
- | b'|'
- | 127.. => return Err(DomainErr::InvalidByte(*byt)),
- _ => (),
- }
- if label_len == 63 {
- Err(DomainErr::LabelLenExceeds63)
- } else {
- label_len += 1;
- Ok(())
- }
- }) {
- Ok(()) => match NonZeroU8::new(label_len) {
- Some(length) => {
- label_lens.push(length);
- Ok(Self {
- value,
- label_lens,
- flag: match flag {
- 0 => CharFlag::None,
- 1 | 4 | 5 => CharFlag::LowerOrTick,
- 2 => CharFlag::Upper,
- 3 => CharFlag::Mixed,
- 6 | 7 => CharFlag::UpperAndTick,
- _ => unreachable!("there is a bug in Domain::try_from_slice"),
- },
- })
+ if label_len == 63 {
+ Err(DomainErr::LabelLenExceeds63)
+ } else {
+ label_len += 1;
+ Ok(())
}
- None => Err(DomainErr::EmptyLabel),
- },
- Err(err) => Err(err),
- }
+ })
+ .and_then(|()| {
+ NonZeroU8::new(label_len)
+ .ok_or(DomainErr::EmptyLabel)
+ .and_then(|length| {
+ Ipv4Addr::parse_ascii(value).map_or_else(
+ |_| {
+ label_lens.push(length);
+ Ok(Self {
+ value,
+ label_lens,
+ flag: match flag {
+ 0 => CharFlag::None,
+ 1 => CharFlag::Lower,
+ 2 => CharFlag::Upper,
+ 3 => CharFlag::LowerUpper,
+ 4 => CharFlag::Ticks,
+ 5 => CharFlag::LowerTicks,
+ 6 => CharFlag::UpperTicks,
+ 7 => CharFlag::All,
+ _ => unreachable!(
+ "there is a bug in Domain::try_from_slice"
+ ),
+ },
+ })
+ },
+ |_| Err(DomainErr::Ipv4),
+ )
+ })
+ })
}
}
}
@@ -328,18 +345,21 @@ fn cmp_doms(left: &Domain<'_>, right: &Domain<'_>) -> Result<(), Ordering> {
let (left_ref, right_ref) = match (left.flag, right.flag) {
(CharFlag::None, _)
| (_, CharFlag::None)
- | (CharFlag::LowerOrTick, CharFlag::LowerOrTick)
+ | (
+ CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks,
+ CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks,
+ )
| (CharFlag::Upper, CharFlag::Upper) => (left, right),
- (CharFlag::LowerOrTick, _) => {
+ (CharFlag::Lower | CharFlag::LowerTicks | CharFlag::Ticks, _) => {
right_input = right.value.to_ascii_lowercase();
right_dom = Domain {
value: right_input.as_slice(),
label_lens: right.label_lens.clone(),
- flag: CharFlag::LowerOrTick,
+ flag: CharFlag::LowerTicks,
};
(left, &right_dom)
}
- (CharFlag::Upper, CharFlag::Mixed) => {
+ (CharFlag::Upper, CharFlag::LowerUpper) => {
right_input = right.value.to_ascii_uppercase();
right_dom = Domain {
value: right_input.as_slice(),
@@ -348,16 +368,16 @@ fn cmp_doms(left: &Domain<'_>, right: &Domain<'_>) -> Result<(), Ordering> {
};
(left, &right_dom)
}
- (_, CharFlag::LowerOrTick) => {
+ (_, CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks) => {
left_input = left.value.to_ascii_lowercase();
left_dom = Domain {
value: left_input.as_slice(),
label_lens: left.label_lens.clone(),
- flag: CharFlag::LowerOrTick,
+ flag: CharFlag::LowerTicks,
};
(&left_dom, right)
}
- (CharFlag::Mixed, CharFlag::Upper) => {
+ (CharFlag::LowerUpper, CharFlag::Upper) => {
left_input = left.value.to_ascii_uppercase();
left_dom = Domain {
value: left_input.as_slice(),
@@ -371,13 +391,13 @@ fn cmp_doms(left: &Domain<'_>, right: &Domain<'_>) -> Result<(), Ordering> {
left_dom = Domain {
value: left_input.as_slice(),
label_lens: left.label_lens.clone(),
- flag: CharFlag::LowerOrTick,
+ flag: CharFlag::LowerTicks,
};
right_input = right.value.to_ascii_lowercase();
right_dom = Domain {
value: right_input.as_slice(),
label_lens: right.label_lens.clone(),
- flag: CharFlag::LowerOrTick,
+ flag: CharFlag::LowerTicks,
};
(&left_dom, &right_dom)
}
@@ -418,7 +438,9 @@ impl Hash for Domain<'_> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
match self.flag {
- CharFlag::None | CharFlag::LowerOrTick => self.value.hash(state),
+ CharFlag::None | CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks => {
+ self.value.hash(state);
+ }
_ => self.value.to_ascii_lowercase().hash(state),
}
}
@@ -2650,6 +2672,10 @@ mod tests {
assert!(Domain::try_from("wwW.ExAMple.COm")
.map_or(false, |d| Domain::try_from("www.example.com")
.map_or(false, |d2| d == d2 && d.cmp(&d2) == Ordering::Equal)));
+ assert!(
+ Domain::try_from("ww_W.com").map_or(false, |d| Domain::try_from("Ww_w.com")
+ .map_or(false, |d2| d == d2 && d.cmp(&d2) == Ordering::Equal))
+ );
// Test valid bytes
let mut vec = Vec::new();
let mut counter = 0;
diff --git a/src/file.rs b/src/file.rs
@@ -858,6 +858,7 @@ impl ExternalFiles {
}
/// Downloads the files from `urls` and converts them to `String`s adding the
/// tasks to `set`.
+ #[allow(clippy::iter_over_hash_type)]
#[inline]
fn get_external_files(
set: &mut JoinSet<Result<File, ExtFileErr>>,