rpz

Response policy zone (RPZ) file generator.
git clone https://git.philomathiclife.com/repos/rpz
Log | Files | Refs | README

commit fde07fe0c0038c76e6ddaa3deeb37db2ce94ab91
parent 8fd8669d36e9f422beae4ce39a643490d6202010
Author: Zack Newman <zack@philomathiclife.com>
Date:   Wed, 21 Aug 2024 18:31:09 -0600

update deps. handle lints

Diffstat:
MCargo.toml | 28++++++++++------------------
Msrc/app.rs | 16+---------------
Msrc/args.rs | 17++++++-----------
Msrc/config.rs | 37++++++++++++++-----------------------
Msrc/dom.rs | 101++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/dom_count_auto_gen.rs | 6+++---
Msrc/file.rs | 47++++++++++++++---------------------------------
Msrc/lib.rs | 8++++----
Msrc/main.rs | 48+++++++++++++++++++++++-------------------------
Msrc/priv_sep.rs | 87+++++++++++++++++++++++++++++++------------------------------------------------
10 files changed, 152 insertions(+), 243 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,37 +9,29 @@ license = "MIT OR Apache-2.0" name = "rpz" readme = "README.md" repository = "https://git.philomathiclife.com/repos/rpz/" -version = "0.6.2" +version = "1.0.0" -[lib] -name = "rpz" -path = "src/lib.rs" - -[[bin]] -name = "rpz" -path = "src/main.rs" +[badges] +maintenance = { status = "actively-developed" } [dependencies] ascii_domain = { version = "0.6.1", default-features = false } -num-bigint = { version = "0.4.5", default-features = false } -reqwest = { version = "0.12.4", default-features = false, features = ["brotli", "deflate", "gzip", "rustls-tls-native-roots", "trust-dns"] } -serde = { version = "1.0.201", default-features = false } +num-bigint = { version = "0.4.6", default-features = false } +reqwest = { version = "0.12.7", default-features = false, features = ["brotli", "deflate", "gzip", "rustls-tls-native-roots", "trust-dns"] } +serde = { version = "1.0.208", default-features = false } superset_map = { version = "0.2.3", default-features = false } -tokio = { version = "1.37.0", default-features = false, features = ["rt", "time"] } -toml = { version = "0.8.12", default-features = false, features = ["parse"] } -url = { version = "2.5.0", default-features = false, features = ["serde"] } +tokio = { version = "1.39.3", default-features = false, features = ["rt", "time"] } +toml = { version = "0.8.19", default-features = false, features = ["parse"] } +url = { version = "2.5.2", default-features = false, features = ["serde"] } zfc = { version = "0.3.2", default-features = false } [target.'cfg(target_os = "openbsd")'.dependencies] -priv_sep = { version = "1.0.1", default-features = false, features = ["openbsd"], optional = true } +priv_sep = { version = "2.0.0", default-features = false, features = ["openbsd"], optional = true } [features] priv_sep = ["dep:priv_sep"] default = ["priv_sep"] -[badges] -maintenance = { status = "actively-developed" } - [profile.release] lto = true panic = 'abort' diff --git a/src/app.rs b/src/app.rs @@ -16,25 +16,21 @@ pub trait Helper { fn kind() -> Kind; } impl Helper for Adblock<'_> { - #[inline] fn kind() -> Kind { Kind::Adblock } } impl Helper for DomainOnly<'_> { - #[inline] fn kind() -> Kind { Kind::DomainOnly } } impl Helper for Hosts<'_> { - #[inline] fn kind() -> Kind { Kind::Hosts } } impl Helper for Wildcard<'_> { - #[inline] fn kind() -> Kind { Kind::Wildcard } @@ -49,12 +45,10 @@ pub struct Domains<'unblock, 'block> { } impl<'unblock, 'block> Domains<'unblock, 'block> { /// Returns a reference to the `RpzDomain`s to block. - #[inline] pub const fn block(&self) -> &SupersetSet<RpzDomain<'block>> { &self.block } /// Returns an empty `Domains`. - #[inline] pub const fn new() -> Self { Self { unblock: SupersetSet::new(), @@ -63,7 +57,6 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { } /// Returns `Domains` based on `LocalFiles` along /// with a `Vec` of `Summary`. - #[inline] pub fn new_with<'c: 'unblock + 'block>( local: &'c LocalFiles, ) -> (Self, Vec<Summary<'c, FirefoxDomainErr>>) { @@ -79,7 +72,6 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { /// a superset of it. /// /// All parsing errors are ignored. - #[inline] fn add_block_file< 'c: 'block, T: Into<RpzDomain<'block>> + ParsedDomain<'block, Error = FirefoxDomainErr> + Helper, @@ -126,7 +118,6 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { /// a superset of it. /// /// All parsing errors are ignored. - #[inline] pub fn add_block_files<'c: 'block>( &mut self, files: &'c Files, @@ -137,8 +128,6 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { /// a superset of it. /// /// All parsing errors are ignored. - #[allow(clippy::into_iter_on_ref)] - #[inline] fn add_files< 'unblock, 'block, @@ -150,7 +139,7 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { summaries: &mut Vec<Summary<'c, FirefoxDomainErr>>, ) { files - .into_iter() + .iter() .fold((), |(), file| doms.add_block_file::<T>(file, summaries)); } add_files::<Adblock<'_>>(self, &files.adblock, summaries); @@ -164,7 +153,6 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { /// `Domains::unblock` does not contain a superset of it. /// /// All parsing errors are ignored. - #[inline] fn add_local_files<'c: 'unblock + 'block>( &mut self, files: &'c LocalFiles, @@ -193,7 +181,6 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { /// # Errors /// /// Returns `Error` iff `writeln` or `fs::rename` do. - #[inline] pub fn write( self, path: Option<(AbsFilePath<false>, AbsFilePath<false>)>, @@ -204,7 +191,6 @@ impl<'unblock, 'block> Domains<'unblock, 'block> { /// so long as `exclude` contains a proper superset of the domain. /// /// Returns the total number of lines written. - #[inline] fn write_domain<W: Write>( doms: &SupersetSet<RpzDomain<'_>>, exclude: Option<&SupersetSet<RpzDomain<'_>>>, diff --git a/src/args.rs b/src/args.rs @@ -1,11 +1,11 @@ -use core::fmt::{self, Display, Formatter}; -use rpz::file::AbsFilePath; -use std::{ - env::{self, Args}, +use core::{ error::Error, + fmt::{self, Display, Formatter}, }; +use rpz::file::AbsFilePath; +use std::env::{self, Args}; /// Error returned when parsing arguments passed to the application. -#[allow(clippy::exhaustive_enums, clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions, reason = "prefer the name")] #[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] pub enum ArgsErr { /// Error when no arguments were passed to the application. @@ -27,8 +27,6 @@ pub enum ArgsErr { QuietAndVerbose, } impl Display for ArgsErr { - #[allow(clippy::ref_patterns)] - #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { Self::NoArgs => write!(f, "no arguments were passed, but at least two are required containing the option '-f' and its value which must be an absolute path to the config file"), @@ -74,13 +72,10 @@ pub enum Opts { } impl Opts { /// Returns `Opts` based on arguments passed to the application. - #[allow(clippy::too_many_lines)] - #[inline] + #[expect(clippy::too_many_lines, reason = "this is fine")] pub fn from_args() -> Result<Self, ArgsErr> { /// Attempts to parse the next `Arg` into `-` or an absolute /// path to a file. - #[allow(clippy::option_if_let_else)] - #[inline] fn get_path(args: &mut Args) -> Result<ConfigPath, ArgsErr> { args.next() .map_or(Err(ArgsErr::ConfigPathNotPassed), |path| { diff --git a/src/config.rs b/src/config.rs @@ -30,14 +30,11 @@ pub struct Config { pub wildcard: HashSet<HttpUrl>, } impl Display for Config { - #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { /// Helper function that writes the `Url`s in a `HashSet<HttpUrl>`. - #[allow(clippy::into_iter_on_ref)] - #[inline] fn keys(set: &HashSet<HttpUrl>, f: &mut Formatter<'_>, name: &str) -> fmt::Result { write!(f, "{name}: [").and_then(|()| { - set.into_iter() + set.iter() .try_fold((), |(), url| write!(f, "{url}, ")) .and_then(|()| f.write_str("], ")) }) @@ -66,8 +63,7 @@ impl Display for Config { } } impl<'de> Deserialize<'de> for Config { - #[allow(clippy::too_many_lines)] - #[inline] + #[expect(clippy::too_many_lines, reason = "this is fine")] fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, @@ -90,7 +86,6 @@ impl<'de> Deserialize<'de> for Config { Wildcard, } impl<'d> Deserialize<'d> for Field { - #[inline] fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'d>, @@ -99,13 +94,11 @@ impl<'de> Deserialize<'de> for Config { struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = Field; - #[inline] fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { formatter.write_str( "'timeout', 'rpz', 'local_dir', 'adblock', 'domain', 'hosts', or 'wildcard'", ) } - #[inline] fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: Error, @@ -129,34 +122,35 @@ impl<'de> Deserialize<'de> for Config { struct ConfigVisitor; impl<'d> Visitor<'d> for ConfigVisitor { type Value = Config; - #[inline] fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { formatter.write_str("struct Config") } - #[allow(clippy::as_conversions, clippy::cast_lossless, clippy::too_many_lines)] - #[inline] + #[expect( + clippy::as_conversions, + clippy::cast_lossless, + clippy::too_many_lines, + reason = "carefull verify use is correct" + )] fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: MapAccess<'d>, { /// Verifies that the `HashSet`s are pairwise disjoint. - #[allow( + #[expect( clippy::arithmetic_side_effects, clippy::indexing_slicing, - clippy::into_iter_on_ref + reason = "carefully verify use is correct" )] - #[inline] fn hash_overlap<E: Error>(maps: &[&HashSet<HttpUrl>]) -> Result<(), E> { /// Verifies the intersection of `left` and `right` is empty. - #[inline] fn url_overlap<E: Error>( left: &HashSet<HttpUrl>, right: &HashSet<HttpUrl>, ) -> Result<(), E> { let (mut iter, urls) = if left.len() <= right.len() { - (left.into_iter(), right) + (left.iter(), right) } else { - (right.into_iter(), left) + (right.iter(), left) }; iter.try_fold((), |(), url| { if urls.contains(url) { @@ -169,16 +163,15 @@ impl<'de> Deserialize<'de> for Config { } }) } - maps.into_iter().enumerate().try_fold((), |(), (idx, map)| { + maps.iter().enumerate().try_fold((), |(), (idx, map)| { maps[idx + 1..] - .into_iter() + .iter() .try_fold((), |(), map2| url_overlap(map, map2)) }) } /// Wrapper around a `HashSet` that is deserializable. struct Urls(HashSet<HttpUrl>); impl<'de> Deserialize<'de> for Urls { - #[inline] fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, @@ -187,11 +180,9 @@ impl<'de> Deserialize<'de> for Config { struct HashVisitor; impl<'d> Visitor<'d> for HashVisitor { type Value = Urls; - #[inline] fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { formatter.write_str("struct Urls") } - #[inline] fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: SeqAccess<'d>, diff --git a/src/dom.rs b/src/dom.rs @@ -22,7 +22,6 @@ use superset_map::SetOrd; use zfc::{BoundedCardinality, Cardinality, Set}; /// Error returned when an invalid string is passed to [`Adblock::parse_value`], [`DomainOnly::parse_value`], /// [`Hosts::parse_value`], [`Wildcard::parse_value`], or [`RpzDomain::parse_value`]. -#[allow(clippy::exhaustive_enums)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum FirefoxDomainErr { /// The domain is invalid based on [`Domain`] using [`ASCII_FIREFOX`]. @@ -56,8 +55,7 @@ impl error::Error for FirefoxDomainErr {} const CHARS: &AllowedAscii<[u8; 78]> = &ASCII_FIREFOX; /// Parses a `[u8]` into a `Domain` using `CHARS` with the added restriction that the `Domain` has a TLD /// that is either all letters or has length of at least five and begins with `b"xn--"`. -#[allow(clippy::indexing_slicing)] -#[inline] +#[expect(clippy::indexing_slicing, reason = "we verify manually")] fn domain_icann_tld<'a: 'b, 'b>(val: &'a [u8]) -> Result<Domain<&'b str>, FirefoxDomainErr> { Domain::try_from_bytes(val, CHARS) .map_err(FirefoxDomainErr::InvalidDomain) @@ -72,7 +70,6 @@ fn domain_icann_tld<'a: 'b, 'b>(val: &'a [u8]) -> Result<Domain<&'b str>, Firefo }) } /// Action taken by a DNS server when a domain matches. -#[allow(clippy::exhaustive_enums)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum RpzAction { /// Send `NXDOMAIN` reply. @@ -147,7 +144,6 @@ where ) } /// Type that can be returned by [`Domain`]-like parsers (e.g., [`Adblock`]). -#[allow(clippy::exhaustive_enums)] #[derive(Clone, Copy, Debug)] pub enum Value<'a, T: ParsedDomain<'a>> { /// The parsed value is a domain. @@ -184,7 +180,7 @@ impl<'a, T: ParsedDomain<'a>> Value<'a, T> { /// # Panics /// /// Panics iff `self` is [`Self::Comment`] or [`Self::Blank`]. - #[allow(clippy::panic)] + #[expect(clippy::panic, reason = "bug if called incorrectly")] #[inline] pub fn unwrap_domain(self) -> T { match self { @@ -199,7 +195,7 @@ impl<'a, T: ParsedDomain<'a>> Value<'a, T> { /// # Panics /// /// Panics iff `self` is [`Self::Domain`] or [`Self::Blank`]. - #[allow(clippy::panic)] + #[expect(clippy::panic, reason = "bug if called incorrectly")] #[inline] pub fn unwrap_comment(self) -> &'a str { match self { @@ -214,7 +210,7 @@ impl<'a, T: ParsedDomain<'a>> Value<'a, T> { /// # Panics /// /// Panics iff `self` is [`Self::Domain`] or [`Self::Comment`]. - #[allow(clippy::panic)] + #[expect(clippy::panic, reason = "bug if called incorrectly")] #[inline] pub fn unwrap_blank(self) { match self { @@ -277,7 +273,6 @@ impl<'a> Adblock<'a> { } /// Since `DomainOnly` and `Hosts` are treated the same, we have this helper function that can be used /// for both. - #[inline] #[must_use] fn cmp_dom(&self, other: &Domain<&str>) -> Ordering { match self.domain.cmp_by_domain_ordering(other) { @@ -303,7 +298,7 @@ impl<'a> Adblock<'a> { /// 1. Pairwise comparisons of each [`ascii_domain::dom::Label`] starting from the TLDs. /// 2. If 1. evaluates as not equivalent, then return the result. /// 3. If `self` represents a single `Domain` (i.e., `!self.is_subdomains()`), - /// then return the comparison of label counts. + /// then return the comparison of label counts. /// 4. `self` is greater. /// /// For example, `com` `<` `example.com` `<` `||example.com` `<` `||com` `<` `net` `<` `example.net` `<` `||example.net` `<` `||net`. @@ -322,7 +317,7 @@ impl<'a> Adblock<'a> { /// 1. Pairwise comparisons of each [`ascii_domain::dom::Label`] starting from the TLDs. /// 2. If 1. evaluates as not equivalent, then return the result. /// 3. If both domains represent a single `Domain`, then return the comparison - /// of label counts. + /// of label counts. /// 4. If one domain represents a single `Domain`, then return that that domain is less. /// 5. If the label counts are the same, `self` is greater. /// 6. Return the inverse of the comparison of label counts. @@ -372,7 +367,7 @@ impl<'a> Adblock<'a> { /// the `Domain` itself when `self.is_subdomains()`. /// /// `!self.is_subdomains()` ⇔ `self.domain_count() == BigUint::new(vec![1])`. - #[allow(clippy::arithmetic_side_effects)] + #[expect(clippy::arithmetic_side_effects, reason = "arbitrary-sized arithmetic")] #[inline] #[must_use] pub fn domain_count(&self) -> BigUint { @@ -473,7 +468,7 @@ impl PartialEq<Adblock<'_>> for &Hosts<'_> { } } impl PartialEq<Wildcard<'_>> for Adblock<'_> { - #[allow(clippy::suspicious_operation_groupings)] + #[expect(clippy::suspicious_operation_groupings, reason = "false positive")] #[inline] fn eq(&self, other: &Wildcard<'_>) -> bool { !(self.subdomains || other.proper_subdomains) && self.domain == other.domain @@ -521,7 +516,7 @@ impl Ord for Adblock<'_> { /// 1. Pairwise comparisons of each [`ascii_domain::dom::Label`] starting from the TLDs. /// 2. If 1. evaluates as not equivalent, then return the result. /// 3. If both domains represent a single `Domain`, then return the comparison - /// of label counts. + /// of label counts. /// 4. If one domain represents a single `Domain`, then return that that domain is less. /// 5. Return the inverse of the comparison of label counts. /// @@ -637,7 +632,11 @@ impl<'a> Deref for Adblock<'a> { } impl<'a> ParsedDomain<'a> for Adblock<'a> { type Error = FirefoxDomainErr; - #[allow(unsafe_code, clippy::indexing_slicing, clippy::into_iter_on_ref)] + #[expect( + unsafe_code, + clippy::indexing_slicing, + reason = "we carefully verify what we are doing" + )] #[inline] fn parse_value<'b: 'a>(val: &'b str) -> Result<Value<'a, Self>, Self::Error> { // First remove leading whitepace. Then check for comments via '#' and '!'. Return Blank iff empty. @@ -675,7 +674,7 @@ impl<'a> ParsedDomain<'a> for Adblock<'a> { }, ); // `Domain`s allow `$`, but we don't want to allow that symbol for Adblock-style rules. - val2.into_iter() + val2.iter() .try_fold((), |(), byt2| { if *byt2 == b'$' { Err(FirefoxDomainErr::InvalidAdblockDomain) @@ -754,7 +753,7 @@ impl<'a> DomainOnly<'a> { /// Same as [`DomainOnly::cardinality`] except that a `NonZeroU8` is returned. /// /// The value is always 1. - #[allow(unsafe_code)] + #[expect(unsafe_code, reason = "trivial use of NonZeroU8::new_unchecked")] #[inline] #[must_use] pub const fn domain_count(&self) -> NonZeroU8 { @@ -927,11 +926,11 @@ impl<'a> Deref for DomainOnly<'a> { } impl<'a> ParsedDomain<'a> for DomainOnly<'a> { type Error = FirefoxDomainErr; - #[allow( + #[expect( unsafe_code, clippy::arithmetic_side_effects, clippy::indexing_slicing, - clippy::into_iter_on_ref + reason = "we verify all use is correct" )] #[inline] fn parse_value<'b: 'a>(val: &'b str) -> Result<Value<'a, Self>, Self::Error> { @@ -948,7 +947,7 @@ impl<'a> ParsedDomain<'a> for DomainOnly<'a> { } else { domain_icann_tld( value[..value - .into_iter() + .iter() .try_fold(0, |i, byt2| if *byt2 == b'#' { Err(i) } else { Ok(i + 1) }) .map_or_else(convert::identity, convert::identity)] .trim_ascii_end(), @@ -1008,7 +1007,7 @@ impl<'a> Hosts<'a> { /// Same as [`Hosts::cardinality`] except that a `NonZeroU8` is returned. /// /// The value is always 1. - #[allow(unsafe_code)] + #[expect(unsafe_code, reason = "trivial use of NonZeroU8::new_unchecked")] #[inline] #[must_use] pub const fn domain_count(&self) -> NonZeroU8 { @@ -1157,11 +1156,11 @@ impl<'a> Deref for Hosts<'a> { } impl<'a> ParsedDomain<'a> for Hosts<'a> { type Error = FirefoxDomainErr; - #[allow( + #[expect( unsafe_code, clippy::arithmetic_side_effects, clippy::indexing_slicing, - clippy::into_iter_on_ref + reason = "carefully verified use is correct" )] #[inline] fn parse_value<'b: 'a>(val: &'b str) -> Result<Value<'a, Self>, Self::Error> { @@ -1214,7 +1213,7 @@ impl<'a> ParsedDomain<'a> for Hosts<'a> { } else { domain_icann_tld( value[..value - .into_iter() + .iter() .try_fold( 0, |i, byt2| if *byt2 == b'#' { Err(i) } else { Ok(i + 1) }, @@ -1274,7 +1273,6 @@ impl<'a> Wildcard<'a> { } /// Since `DomainOnly` and `Hosts` are treated the same, we have this helper function that can be used /// for both. - #[inline] #[must_use] fn cmp_dom(&self, other: &Domain<&str>) -> Ordering { match self.domain.cmp_by_domain_ordering(other) { @@ -1300,7 +1298,7 @@ impl<'a> Wildcard<'a> { /// 1. Pairwise comparisons of each [`ascii_domain::dom::Label`] starting from the TLDs. /// 2. If 1. evaluates as not equivalent, then return the result. /// 3. If `self` represents a single `Domain` (i.e., `!self.is_proper_subdomains()`), - /// then return the comparison of label counts. + /// then return the comparison of label counts. /// 4. Return `self` is greater. /// /// For example, `com` `<` `example.com` `<` `*.example.com` `<` `*.com` `<` `net` `<` `example.net` `<` `*.example.net` `<` `*.net`. @@ -1383,7 +1381,7 @@ impl Ord for Wildcard<'_> { /// 1. Pairwise comparisons of each [`ascii_domain::dom::Label`] starting from the TLDs. /// 2. If 1. evaluates as not equivalent, then return the result. /// 3. If both domains represent a single `Domain`, then return the comparison - /// of label counts. + /// of label counts. /// 4. If one domain represents a single `Domain`, then return that that domain is less. /// 5. Return the inverse of the comparison of label counts. /// @@ -1503,11 +1501,11 @@ impl<'a> Deref for Wildcard<'a> { } impl<'a> ParsedDomain<'a> for Wildcard<'a> { type Error = FirefoxDomainErr; - #[allow( + #[expect( unsafe_code, clippy::arithmetic_side_effects, clippy::indexing_slicing, - clippy::into_iter_on_ref + reason = "need them all. care has been taken." )] #[inline] fn parse_value<'b: 'a>(val: &'b str) -> Result<Value<'a, Self>, Self::Error> { @@ -1534,7 +1532,7 @@ impl<'a> ParsedDomain<'a> for Wildcard<'a> { ); domain_icann_tld( val2[..val2 - .into_iter() + .iter() .try_fold(0, |i, byt2| if *byt2 == b'#' { Err(i) } else { Ok(i + 1) }) .map_or_else(convert::identity, convert::identity)] .trim_ascii_end(), @@ -1570,7 +1568,6 @@ impl<'a> ParsedDomain<'a> for Wildcard<'a> { } } /// A [`Domain`] in a [response policy zone (RPZ)](https://en.wikipedia.org/wiki/Response_policy_zone) file. -#[allow(clippy::exhaustive_enums)] #[derive(Clone, Debug)] pub enum RpzDomain<'a> { /// An `Adblock` domain. @@ -1584,7 +1581,6 @@ pub enum RpzDomain<'a> { } impl<'a> RpzDomain<'a> { /// Returns `true` iff `self` represents a single [`Domain`]. - #[allow(clippy::ref_patterns)] #[inline] #[must_use] pub const fn is_domain(&self) -> bool { @@ -1596,7 +1592,6 @@ impl<'a> RpzDomain<'a> { } /// Returns `true` iff `self` represents proper subdomains of the contained [`Domain`] (i.e., /// is a [`Wildcard`] such that [`Wildcard::is_proper_subdomains`]). - #[allow(clippy::ref_patterns)] #[inline] #[must_use] pub const fn is_proper_subdomains(&self) -> bool { @@ -1607,7 +1602,6 @@ impl<'a> RpzDomain<'a> { } /// Returns `true` iff `self` represents subdomains of the contained [`Domain`] (i.e., is an /// [`Adblock`] such that [`Adblock::is_subdomains`]). - #[allow(clippy::ref_patterns)] #[inline] #[must_use] pub const fn is_subdomains(&self) -> bool { @@ -1618,7 +1612,6 @@ impl<'a> RpzDomain<'a> { } /// Returns the count of [`Domain`]s represented by `self`. This function is the same as /// [`RpzDomain::cardinality`] except that it returns a `BigUint`. - #[allow(clippy::ref_patterns)] #[inline] #[must_use] pub fn domain_count(&self) -> BigUint { @@ -1631,7 +1624,6 @@ impl<'a> RpzDomain<'a> { } } impl PartialEq<RpzDomain<'_>> for RpzDomain<'_> { - #[allow(clippy::ref_patterns)] #[inline] fn eq(&self, other: &RpzDomain<'_>) -> bool { match *self { @@ -1692,7 +1684,7 @@ impl Ord for RpzDomain<'_> { /// 1. Pairwise comparisons of each [`ascii_domain::dom::Label`] starting from the TLDs. /// 2. If 1. evaluates as not equivalent, then return the result. /// 3. If both domains represent a single `Domain`, then return the comparison - /// of label counts. + /// of label counts. /// 4. If one domain represents a single `Domain`, then return that that domain is less. /// 5. If the label counts are the same and exactly one domain represents proper subdomains, the other domain is greater. /// 6. Return the inverse of the comparison of label counts. @@ -1701,7 +1693,6 @@ impl Ord for RpzDomain<'_> { /// ascending order: /// /// `bar.com`, `www.bar.com`, `*.www.bar.com`, `||www.bar.com`, `*.bar.com`, `||bar.com`, `example.com`, `www.example.com`, `*.www.example.com`, `||www.example.com`, `*.example.com`, `||example.com`, `foo.com`, `www.foo.com`, `*.foo.com`, `*.com`, `example.net`, `*.net` - #[allow(clippy::ref_patterns)] #[inline] fn cmp(&self, other: &Self) -> Ordering { match *self { @@ -1733,7 +1724,6 @@ impl Ord for RpzDomain<'_> { } } impl Display for RpzDomain<'_> { - #[allow(clippy::ref_patterns)] #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { @@ -1754,7 +1744,6 @@ impl<'a> Set for RpzDomain<'a> { fn cardinality(&self) -> Option<Cardinality> { Some(Cardinality::Finite(self.domain_count())) } - #[allow(clippy::ref_patterns)] #[inline] fn contains<Q>(&self, elem: &Q) -> bool where @@ -1767,7 +1756,6 @@ impl<'a> Set for RpzDomain<'a> { Self::Wildcard(ref dom) => dom.contains(elem), } } - #[allow(clippy::ref_patterns)] #[inline] fn is_proper_subset(&self, val: &Self) -> bool { match *val { @@ -1824,7 +1812,6 @@ impl<'a> Set for RpzDomain<'a> { impl SetOrd for RpzDomain<'_> {} impl<'a> Deref for RpzDomain<'a> { type Target = Domain<&'a str>; - #[allow(clippy::ref_patterns)] #[inline] fn deref(&self) -> &Self::Target { match *self { @@ -1862,21 +1849,21 @@ impl<'a: 'b, 'b> From<Wildcard<'a>> for RpzDomain<'b> { impl<'a> ParsedDomain<'a> for RpzDomain<'a> { type Error = FirefoxDomainErr; #[inline] - fn parse_value<'b: 'a>(value: &'b str) -> Result<Value<'a, Self>, Self::Error> { - DomainOnly::parse_value(value).map_or_else( + fn parse_value<'b: 'a>(val: &'b str) -> Result<Value<'a, Self>, Self::Error> { + DomainOnly::parse_value(val).map_or_else( |_| { - Hosts::parse_value(value).map_or_else( + Hosts::parse_value(val).map_or_else( |_| { - Wildcard::parse_value(value).map_or_else( + Wildcard::parse_value(val).map_or_else( |_| { - Adblock::parse_value(value).map(|val| match val { + Adblock::parse_value(val).map(|value| match value { Value::Domain(dom) => Value::Domain(Self::Adblock(dom)), Value::Comment(com) => Value::Comment(com), Value::Blank => Value::Blank, }) }, - |val| { - Ok(match val { + |value| { + Ok(match value { Value::Domain(dom) => Value::Domain(Self::Wildcard(dom)), Value::Comment(com) => Value::Comment(com), Value::Blank => Value::Blank, @@ -1884,8 +1871,8 @@ impl<'a> ParsedDomain<'a> for RpzDomain<'a> { }, ) }, - |val| { - Ok(match val { + |value| { + Ok(match value { Value::Domain(dom) => Value::Domain(Self::Hosts(dom)), Value::Comment(com) => Value::Comment(com), Value::Blank => Value::Blank, @@ -1893,8 +1880,8 @@ impl<'a> ParsedDomain<'a> for RpzDomain<'a> { }, ) }, - |val| { - Ok(match val { + |value| { + Ok(match value { Value::Domain(dom) => Value::Domain(Self::DomainOnly(dom)), Value::Comment(com) => Value::Comment(com), Value::Blank => Value::Blank, @@ -1902,7 +1889,6 @@ impl<'a> ParsedDomain<'a> for RpzDomain<'a> { }, ) } - #[allow(clippy::ref_patterns)] #[inline] fn domain(&self) -> &Domain<&'a str> { match *self { @@ -1912,7 +1898,6 @@ impl<'a> ParsedDomain<'a> for RpzDomain<'a> { Self::Wildcard(ref dom) => &dom.domain, } } - #[allow(clippy::ref_patterns)] #[inline] fn write_to_rpz<W: Write>(&self, action: RpzAction, writer: W) -> Result<(), Error> { match *self { @@ -2194,7 +2179,7 @@ mod tests { assert!(Wildcard::parse_value("*.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").map_or_else(|e| e == FirefoxDomainErr::InvalidWildcardDomain, |_| false)); // Test 126 labels after wildcard is ok. assert!(Wildcard::parse_value("*.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").map_or(false, |val| match val { - Value::Domain(ref dom) => dom.domain.into_iter().count() == 126 && dom.proper_subdomains, + Value::Domain(ref dom) => dom.domain.iter().count() == 126 && dom.proper_subdomains, Value::Comment(_) | Value::Blank => false, })); // Test comment. @@ -2314,9 +2299,9 @@ mod tests { // We can have two labels each with one character, // one label with one to three characters, or 0 labels. // This is 1 + 52 + 52^2 + 52^3 + 52^2 = (1-52^4)/(1-52) + 52^2 = (52^4 - 1)/51 + 52^2 = 146069. - assert!(Adblock::parse_value("||a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").map_or(false, |val| { let dom = val.unwrap_domain(); dom.domain.len().get() == 249 && dom.domain.into_iter().count() == 125 && dom.domain_count() == BigUint::new(vec![146069]) })); + assert!(Adblock::parse_value("||a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").map_or(false, |val| { let dom = val.unwrap_domain(); dom.domain.len().get() == 249 && dom.domain.iter().count() == 125 && dom.domain_count() == BigUint::new(vec![146069]) })); // A subdomain of length 252 or 253 gets converted to a domain. - assert!(Adblock::parse_value("||a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").map_or(false, |val| { let dom = val.unwrap_domain(); dom.domain.into_iter().count() == 127 && !dom.subdomains && dom.domain_count() == BigUint::new(vec![1]) })); + assert!(Adblock::parse_value("||a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").map_or(false, |val| { let dom = val.unwrap_domain(); dom.domain.iter().count() == 127 && !dom.subdomains && dom.domain_count() == BigUint::new(vec![1]) })); // Pre-calculated manually. // This is the number of domains possible between 2 and 252 characters. assert!(Wildcard::parse_value("*.a").map_or(false, |val| { diff --git a/src/dom_count_auto_gen.rs b/src/dom_count_auto_gen.rs @@ -2,14 +2,14 @@ use ascii_domain::dom::Domain; use num_bigint::BigUint; /// The count of proper subdomains for both `Adblock` and `Wildcard` `Domain` when subdomains /// and proper subdomains are represented respectively. -#[allow( +#[expect( clippy::arithmetic_side_effects, clippy::as_conversions, clippy::indexing_slicing, clippy::too_many_lines, - clippy::unreadable_literal + clippy::unreadable_literal, + reason = "autogenerated code and need it all" )] -#[inline] pub fn proper_subdomain_count(dom: &Domain<&str>) -> BigUint { // The commented out code at the end of the function was used to calculate the cardinalities // for each possible value of domain length; however it takes as much as 16 seconds to calculate diff --git a/src/file.rs b/src/file.rs @@ -5,6 +5,7 @@ use crate::dom::{ use alloc::string::FromUtf8Error; use core::{ borrow::Borrow, + error::Error, fmt::{self, Display, Formatter}, hash::Hash, ops::Deref, @@ -14,7 +15,6 @@ use reqwest::Client; use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor}; use std::{ collections::{HashMap, HashSet}, - error::Error, fs, io::{self, ErrorKind}, path::{Path, PathBuf}, @@ -80,7 +80,7 @@ impl<const IS_DIR: bool> AbsFilePath<IS_DIR> { /// /// If `IS_DIR` and `PathBuf::from(val).as_bytes().last().unwrap() != b'/'`, `val` /// will have `/` appended to it. - #[allow(clippy::option_if_let_else)] + #[expect(clippy::option_if_let_else, reason = "map will not work")] #[inline] #[must_use] pub fn from_string(val: String) -> Option<Self> { @@ -131,7 +131,7 @@ impl<const IS_DIR: bool> From<AbsFilePath<IS_DIR>> for PathBuf { } } impl<'de, const IS_DIR: bool> Deserialize<'de> for AbsFilePath<IS_DIR> { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines, reason = "this is fine")] #[inline] fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where @@ -141,12 +141,10 @@ impl<'de, const IS_DIR: bool> Deserialize<'de> for AbsFilePath<IS_DIR> { struct FilePathVisitor<const IS_DIR: bool>; impl<'de, const IS_DIR: bool> Visitor<'de> for FilePathVisitor<IS_DIR> { type Value = AbsFilePath<IS_DIR>; - #[inline] fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { formatter.write_str("struct AbsFilePath") } - #[allow(clippy::arithmetic_side_effects)] - #[inline] + #[expect(clippy::arithmetic_side_effects, reason = "math has been verified")] fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: de::Error, @@ -161,8 +159,7 @@ impl<'de, const IS_DIR: bool> Deserialize<'de> for AbsFilePath<IS_DIR> { |byt| { if *byt == b'/' { if IS_DIR { - let mut path = PathBuf::with_capacity(v.len()); - path.push(v); + let path = PathBuf::with_capacity(v.len()).join(v); if path.is_absolute() { Ok(AbsFilePath { path }) } else { @@ -178,8 +175,7 @@ impl<'de, const IS_DIR: bool> Deserialize<'de> for AbsFilePath<IS_DIR> { )) } } else if IS_DIR { - let mut path = PathBuf::with_capacity(v.len() + 1); - path.push(v); + let mut path = PathBuf::with_capacity(v.len() + 1).join(v); path.as_mut_os_string().push("/"); if path.is_absolute() { Ok(AbsFilePath { path }) @@ -190,8 +186,7 @@ impl<'de, const IS_DIR: bool> Deserialize<'de> for AbsFilePath<IS_DIR> { )) } } else { - let mut path = PathBuf::with_capacity(v.len()); - path.push(v); + let path = PathBuf::with_capacity(v.len()).join(v); if path.is_absolute() && path.file_name().is_some() { Ok(AbsFilePath { path }) } else { @@ -204,7 +199,6 @@ impl<'de, const IS_DIR: bool> Deserialize<'de> for AbsFilePath<IS_DIR> { }, ) } - #[inline] fn visit_string<E>(self, v: String) -> Result<Self::Value, E> where E: de::Error, @@ -329,11 +323,9 @@ impl<'de> Deserialize<'de> for HttpUrl { struct UrlVisitor; impl<'d> Visitor<'d> for UrlVisitor { type Value = HttpUrl; - #[inline] fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { formatter.write_str("struct HttpUrl") } - #[inline] fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: de::Error, @@ -363,7 +355,6 @@ impl<'de> Deserialize<'de> for HttpUrl { } /// Represents the kind of [`ParsedDomain`]s a [`File`] /// contains. -#[allow(clippy::exhaustive_enums)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Kind { /// [`Adblock`] domains. @@ -387,7 +378,6 @@ impl Display for Kind { } } /// The name where a [`File`] was sourced from. -#[allow(clippy::exhaustive_enums)] #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Name { /// The `File` came from the contained `AbsFilePath`. @@ -396,7 +386,6 @@ pub enum Name { Url(HttpUrl), } impl Display for Name { - #[allow(clippy::ref_patterns)] #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { @@ -487,25 +476,21 @@ trait Helper { fn kind() -> Kind; } impl Helper for Adblock<'_> { - #[inline] fn kind() -> Kind { Kind::Adblock } } impl Helper for DomainOnly<'_> { - #[inline] fn kind() -> Kind { Kind::DomainOnly } } impl Helper for Hosts<'_> { - #[inline] fn kind() -> Kind { Kind::Hosts } } impl Helper for Wildcard<'_> { - #[inline] fn kind() -> Kind { Kind::Wildcard } @@ -576,7 +561,7 @@ impl Files { /// /// Returns a `Vec` containing `Summary` information for each /// [`File`] that was parsed. - #[allow(clippy::arithmetic_side_effects, clippy::into_iter_on_ref)] + #[expect(clippy::arithmetic_side_effects, reason = "math is verified")] #[inline] pub fn add_to_superset<'a: 'b, 'b>( &'a self, @@ -584,7 +569,6 @@ impl Files { ) -> Vec<Summary<'a, FirefoxDomainErr>> { /// Iterates each `String` from `files` and transforms each line /// into `T` before adding it as an `RpzDomain` into `doms`. - #[inline] fn insert< 'a, 'b: 'a, @@ -595,7 +579,7 @@ impl Files { summaries: &mut Vec<Summary<'b, FirefoxDomainErr>>, ) { let kind = T::kind(); - files.into_iter().fold((), |(), file| { + files.iter().fold((), |(), file| { let mut summary = Summary { file, kind, @@ -668,11 +652,13 @@ impl LocalFiles { /// /// Returns [`io::Error`] iff reading said files causes an error. Note that /// it is _not_ an error if a directory does not exist. - #[allow(clippy::wildcard_enum_match_arm)] + #[expect( + clippy::wildcard_enum_match_arm, + reason = "too many to enumerate manually" + )] #[inline] pub fn from_path(dir: AbsFilePath<true>) -> Result<Option<Self>, io::Error> { /// Checks if `path` exists. - #[inline] fn exists<P: AsRef<Path>>(path: P) -> Result<bool, io::Error> { fs::metadata(path).map_or_else( |err| match err.kind() { @@ -684,14 +670,12 @@ impl LocalFiles { } /// Reads all files stored in `adblock/`, `domain/`, `hosts/`, and `wildcard/` /// directories under `dir/name/`. - #[inline] fn get_files( mut dir: PathBuf, files: &mut Files, name: &str, ) -> Result<PathBuf, io::Error> { /// Reads all files under `dir/name/`. - #[inline] fn get_file( files: &mut Vec<File>, mut dir: PathBuf, @@ -742,7 +726,6 @@ impl LocalFiles { } } /// Error returned when downloading text files from HTTP(S) servers. -#[allow(clippy::exhaustive_enums)] #[derive(Debug)] pub enum ExtFileErr { /// Error when a task exceeds the specified timeout. @@ -755,7 +738,6 @@ pub enum ExtFileErr { InvalidUtf8(FromUtf8Error), } impl Display for ExtFileErr { - #[allow(clippy::ref_patterns)] #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { @@ -855,8 +837,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] + #[expect(clippy::iter_over_hash_type, reason = "order does not matter")] fn get_external_files( set: &mut JoinSet<Result<File, ExtFileErr>>, client: &'static Client, diff --git a/src/lib.rs b/src/lib.rs @@ -10,7 +10,6 @@ //! ad-blocking files into a [response policy zone (RPZ)](https://en.wikipedia.org/wiki/Response_policy_zone) //! file easier. #![feature(btree_cursors)] -#![feature(byte_slice_trim_ascii)] #![feature(io_error_more)] #![cfg_attr(doc, feature(doc_auto_cfg))] #![deny( @@ -36,17 +35,18 @@ clippy::style, clippy::suspicious )] -#![allow( +#![expect( clippy::blanket_clippy_restriction_lints, + clippy::exhaustive_enums, clippy::exhaustive_structs, clippy::implicit_return, clippy::min_ident_chars, clippy::missing_trait_methods, clippy::multiple_crate_versions, clippy::question_mark_used, - clippy::single_call_fn, + clippy::ref_patterns, clippy::single_char_lifetime_names, - clippy::unseparated_literal_suffix + reason = "never want to use these lints" )] /// Module for hostname-like domains including parsing [`str`]s /// from a variety of formats. diff --git a/src/main.rs b/src/main.rs @@ -26,16 +26,18 @@ clippy::style, clippy::suspicious )] -#![allow( +#![expect( clippy::blanket_clippy_restriction_lints, clippy::implicit_return, clippy::min_ident_chars, clippy::missing_trait_methods, clippy::multiple_crate_versions, clippy::question_mark_used, + clippy::ref_patterns, clippy::single_call_fn, clippy::single_char_lifetime_names, - clippy::unseparated_literal_suffix + clippy::unseparated_literal_suffix, + reason = "never want to use these lints" )] /// Contains a wrapper of block and unblock `RpzDomain`s /// which can be used to write to a `File` or `stdout`. @@ -44,7 +46,6 @@ mod app; mod args; /// Module for the TOML config file. mod config; -#[allow(clippy::doc_markdown)] /// Contains functions for `pledge(2)` and `unveil(2)` on OpenBSD platforms when compiled /// with the `priv_sep` feature; otherwise almost all functions are no-ops. mod priv_sep; @@ -54,6 +55,7 @@ use crate::{ config::Config, }; use core::{ + error::Error, fmt::{self, Display, Formatter}, time::Duration, }; @@ -66,7 +68,6 @@ use rpz::{ }; use std::{ collections::HashSet, - error::Error, fs, io::{self, Read, Write}, sync::OnceLock, @@ -94,7 +95,6 @@ const VERSION: &str = concat!("rpz ", env!("CARGO_PKG_VERSION")); /// The User-Agent header value sent to HTTP(S) servers. const USER_AGENT: &str = concat!("rpz/", env!("CARGO_PKG_VERSION")); /// Error returned from the program. -#[allow(clippy::exhaustive_enums)] enum E { /// Variant for errors due to incorrect arguments being passed. Args(ArgsErr), @@ -111,8 +111,6 @@ enum E { NoBlockEntries, } impl fmt::Debug for E { - #[allow(clippy::ref_patterns)] - #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { Self::Args(ref e) => write!(f, "{e}.\nFor more information, try '--help'."), @@ -125,8 +123,6 @@ impl fmt::Debug for E { } } impl Display for E { - #[allow(clippy::ref_patterns)] - #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { Self::Args(ref e) => e.fmt(f), @@ -141,45 +137,38 @@ impl Display for E { } impl Error for E {} impl From<ArgsErr> for E { - #[inline] fn from(value: ArgsErr) -> Self { Self::Args(value) } } impl From<de::Error> for E { - #[inline] fn from(value: de::Error) -> Self { Self::Config(value) } } impl From<io::Error> for E { - #[inline] fn from(value: io::Error) -> Self { Self::Io(value) } } impl From<ExtFileErr> for E { - #[inline] fn from(value: ExtFileErr) -> Self { Self::ExtFile(value) } } #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] impl From<UnveilErr> for E { - #[inline] fn from(value: UnveilErr) -> Self { Self::Unveil(value) } } #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] impl From<!> for E { - #[inline] fn from(value: !) -> Self { value } } /// Reads `Config` from `conf`. -#[inline] fn get_config(conf: ConfigPath) -> Result<Config, E> { toml::from_str::<Config>( match conf { @@ -200,7 +189,6 @@ fn get_config(conf: ConfigPath) -> Result<Config, E> { .map_err(E::Config) } /// Gets `LocalFiles` from `local_dir`. -#[inline] fn get_local_files(local_dir: Option<AbsFilePath<true>>) -> Result<Option<LocalFiles>, E> { local_dir.map_or_else( || Ok(None), @@ -222,8 +210,7 @@ fn get_local_files(local_dir: Option<AbsFilePath<true>>) -> Result<Option<LocalF ) } /// Downloads block files from HTTP(S) servers. -#[allow(clippy::unwrap_used)] -#[inline] +#[expect(clippy::unreachable, reason = "there is a bug and we want to crash")] fn get_external_files( timeout: Duration, adblock: HashSet<HttpUrl>, @@ -249,8 +236,12 @@ fn get_external_files( .build() .map_err(ExtFileErr::Http)?, ) - .unwrap(); - let client = CLIENT.get().unwrap(); + .unwrap_or_else(|_e| { + unreachable!("there is a bug in OnceLock::set") + }); + let client = CLIENT + .get() + .unwrap_or_else(|| unreachable!("there is a bug in OnceLock::get")); files.add_adblock(client, adblock); files.add_domain(client, domain); files.add_hosts(client, hosts); @@ -277,7 +268,6 @@ enum Verbosity { } /// Writes to `stdout` the summary information in the event the quiet /// option was not passed. -#[inline] fn write_summary( summaries: Vec<Summary<'_, FirefoxDomainErr>>, verbose: bool, @@ -317,7 +307,11 @@ fn write_summary( error_count, ) } -#[allow(clippy::arithmetic_side_effects, clippy::unwrap_used)] +#[expect( + clippy::arithmetic_side_effects, + clippy::unreachable, + reason = "math is correct and we want to crash if there is a bug" +)] fn main() -> Result<(), E> { let mut promises = priv_sep::pledge_init()?; priv_sep::veil_all()?; @@ -399,8 +393,12 @@ fn main() -> Result<(), E> { if domains.block().is_empty() { return Err(E::NoBlockEntries); } - let (unblock_count, block_count) = - domains.write(config.rpz.map(|file| (file, tmp_rpz.unwrap())))?; + let (unblock_count, block_count) = domains.write(config.rpz.map(|file| { + ( + file, + tmp_rpz.unwrap_or_else(|| unreachable!("there is a bug in main")), + ) + }))?; if matches!(verbosity, Verbosity::None) { Ok(()) } else { diff --git a/src/priv_sep.rs b/src/priv_sep.rs @@ -1,7 +1,7 @@ #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] pub use priv_sep::UnveilErr; #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -use priv_sep::{self, Permissions, Promise, Promises}; +use priv_sep::{self, Permission, Permissions, Promise, Promises}; #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] use std::env; #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] @@ -20,8 +20,7 @@ pub struct Zst; /// to run. Specifically, the `Promise`s `Cpath`, `Dns`, `Inet`, `Rpath`, `Stdio`, `Unveil`, and `Wpath` /// are passed. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] -pub fn pledge_init() -> Result<Promises<7>, Error> { +pub fn pledge_init() -> Result<Promises, Error> { let promises = Promises::new([ Promise::Cpath, Promise::Dns, @@ -37,9 +36,8 @@ pub fn pledge_init() -> Result<Promises<7>, Error> { } } /// No-op that always returns `Ok`. -#[allow(clippy::unnecessary_wraps)] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[inline] pub const fn pledge_init() -> Result<Zst, !> { Ok(Zst) } @@ -48,104 +46,85 @@ pub const fn pledge_init() -> Result<Zst, !> { /// This should only be called when `stdout` is written to /// instead of an RPZ file. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] -pub fn pledge_away_create_write(promises: &mut Promises<7>) -> Result<(), Error> { - promises.remove_promises([Promise::Cpath, Promise::Wpath]); - promises.pledge() +pub fn pledge_away_create_write(promises: &mut Promises) -> Result<(), Error> { + promises.remove_promises_then_pledge([Promise::Cpath, Promise::Wpath]) } /// No-op that always returns `Ok`. -#[allow(clippy::unnecessary_wraps)] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[inline] pub fn pledge_away_create_write(_: &mut Zst) -> Result<(), !> { Ok(()) } /// Removes `Promise::Unveil`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] -pub fn pledge_away_unveil(promises: &mut Promises<7>) -> Result<(), Error> { - promises.remove(Promise::Unveil); - promises.pledge() +pub fn pledge_away_unveil(promises: &mut Promises) -> Result<(), Error> { + promises.remove_then_pledge(Promise::Unveil) } /// No-op that always returns `Ok`. -#[allow(clippy::unnecessary_wraps)] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[inline] pub fn pledge_away_unveil(_: &mut Zst) -> Result<(), !> { Ok(()) } /// Removes `Promise::Dns` and `Promise::Inet`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] -pub fn pledge_away_net(promises: &mut Promises<7>) -> Result<(), Error> { - promises.remove_promises(Promises::new([Promise::Dns, Promise::Inet])); - promises.pledge() +pub fn pledge_away_net(promises: &mut Promises) -> Result<(), Error> { + promises.remove_promises_then_pledge([Promise::Dns, Promise::Inet]) } /// No-op that always returns `Ok`. -#[allow(clippy::unnecessary_wraps)] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[inline] pub fn pledge_away_net(_: &mut Zst) -> Result<(), !> { Ok(()) } /// Removes all `Promise`s except `Stdio`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] -pub fn pledge_away_all_but_stdio(promises: &mut Promises<7>) -> Result<(), Error> { - promises.retain([Promise::Stdio]); - promises.pledge() +pub fn pledge_away_all_but_stdio(promises: &mut Promises) -> Result<(), Error> { + promises.retain_then_pledge([Promise::Stdio]) } /// No-op that always returns `Ok`. -#[allow(clippy::unnecessary_wraps)] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[inline] pub fn pledge_away_all_but_stdio(_: &mut Zst) -> Result<(), !> { Ok(()) } /// Calls `unveil`_on `path` with no `Permissions`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] pub fn unveil_none<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { Permissions::NONE.unveil(path) } /// No-op that always returns `Ok`. #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[allow(clippy::unnecessary_wraps)] -#[inline] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] pub fn unveil_none<P: AsRef<Path>>(_: P) -> Result<(), !> { Ok(()) } /// Calls `unveil_none` on `/`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] pub fn veil_all() -> Result<(), UnveilErr> { unveil_none("/") } /// No-op that always returns `Ok`. #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[allow(clippy::unnecessary_wraps)] -#[inline] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] pub const fn veil_all() -> Result<(), !> { Ok(()) } /// Calls `unveil`_on `path` with `Permissions::CREATE`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] pub fn unveil_create<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { Permissions::CREATE.unveil(path) } /// No-op that always returns `Ok`. #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[allow(clippy::unnecessary_wraps)] -#[inline] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] pub fn unveil_create<P: AsRef<Path>>(_: P) -> Result<(), !> { Ok(()) } /// Calls `unveil`_on `path` with `Permissions::READ` and returns /// `true` iff `path` exists. -#[allow(clippy::wildcard_enum_match_arm)] +#[expect(clippy::wildcard_enum_match_arm)] #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] pub fn unveil_read_dir<P: AsRef<Path>>(path: P) -> Result<bool, UnveilErr> { Permissions::READ.unveil(path).map_or_else( |err| match err { @@ -159,9 +138,11 @@ pub fn unveil_read_dir<P: AsRef<Path>>(path: P) -> Result<bool, UnveilErr> { ) } /// Returns `true` iff `path` exists -#[allow(clippy::wildcard_enum_match_arm)] +#[expect( + clippy::wildcard_enum_match_arm, + reason = "too many branches to write out manually" +)] #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[inline] pub fn unveil_read_dir<P: AsRef<Path>>(path: P) -> Result<bool, Error> { fs::metadata(path).map_or_else( |err| match err.kind() { @@ -173,14 +154,12 @@ pub fn unveil_read_dir<P: AsRef<Path>>(path: P) -> Result<bool, Error> { } /// Calls `unveil`_on `path` with `Permissions::READ`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] pub fn unveil_read_file<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { Permissions::READ.unveil(path) } /// No-op that always returns `Ok`. #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[allow(clippy::unnecessary_wraps)] -#[inline] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] pub fn unveil_read_file<P: AsRef<Path>>(_: P) -> Result<(), !> { Ok(()) } @@ -188,32 +167,34 @@ pub fn unveil_read_file<P: AsRef<Path>>(_: P) -> Result<(), !> { /// in addition to setting the `SSL_CERT_FILE` variable to `/etc/ssl/cert.pem`. /// We rely on `SSL_CERT_FILE` as that allows one to `unveil(2)` only `/etc/ssl/cert.pem` /// instead of `/etc/ssl/`. +#[expect(unsafe_code)] #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] pub fn unveil_https() -> Result<(), UnveilErr> { /// The path to the root certificate store. const CERTS: &str = "/etc/ssl/cert.pem"; - unveil_read_file(CERTS).map(|()| env::set_var("SSL_CERT_FILE", CERTS)) + unveil_read_file(CERTS).map(|()| { + // SAFETY: + // `unveil_https` is only called in `super::main` + // in a single-threaded context; thus this is OK. + unsafe { env::set_var("SSL_CERT_FILE", CERTS) } + }) } /// No-op that always returns `Ok`. #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[allow(clippy::unnecessary_wraps)] -#[inline] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] pub const fn unveil_https() -> Result<(), !> { Ok(()) } /// Calls `unveil`_on `path` with create, read, and write `Permissions`. #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -#[inline] pub fn unveil_create_read_write<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { let mut perms = Permissions::ALL; - perms.execute = false; + perms.disable(Permission::Execute); perms.unveil(path) } /// No-op that always returns `Ok`. #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] -#[allow(clippy::unnecessary_wraps)] -#[inline] +#[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] pub fn unveil_create_read_write<P: AsRef<Path>>(_: P) -> Result<(), !> { Ok(()) }