Follow the Rust style guide's smaller rules (#353)

Measured against /src/styleguide/rustguide.md:
- Each use statement imports from one module, and free functions are
  called through theirs (db::step, db::now, jsonwebtoken::decode,
  crate::feed::redact, unix::signal) rather than imported bare.
- unsafe_code is forbidden workspace-wide. The one unsafe was a test's
  set_var, which is unsound while other tests' threads read the
  environment; the test now reads PATH, which every run has.
- Locks say why they may panic: expect("a panic elsewhere poisoned this
  lock") in place of 25 unwraps, and unique_slug's endless search says
  it cannot end.
- No combinator chains in for headers: each is a named variable first.
- #[must_use] on the 27 functions clippy's must_use_candidate names.
- A literal bool passed where its meaning is not plain gets its name
  (/* keep_open */ true, /* piece */ false, ...).

No behaviour changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 01:07:09 +00:00
parent 59c7f5f213
commit 41affc8d8e
25 changed files with 202 additions and 129 deletions

View File

@@ -28,6 +28,7 @@ impl Allowed {
list.iter().map(|s| parse_net(s)).collect::<Result<Vec<_>>>().map(|v| Self(Arc::new(v)))
}
#[must_use]
pub fn permits(&self, ip: IpAddr) -> bool {
let ip = canonical(ip);
!is_internal(ip) || self.0.iter().any(|&(net, bits)| contains(net, bits, ip))
@@ -73,6 +74,7 @@ fn contains(net: IpAddr, bits: u8, ip: IpAddr) -> bool {
/// unstable.
// shortcut: 6to4 (2002::/16) and Teredo addresses that wrap an internal IPv4 one pass; check the
// embedded address if an IPv6 route to them ever exists here.
#[must_use]
pub fn is_internal(ip: IpAddr) -> bool {
match canonical(ip) {
IpAddr::V4(a) => {
@@ -107,6 +109,7 @@ pub fn is_internal(ip: IpAddr) -> bool {
pub struct Guard(Allowed);
impl Guard {
#[must_use]
pub fn new(allowed: Allowed) -> Self {
Self(allowed)
}

View File

@@ -87,6 +87,7 @@ pub static FETCHED_BYTES: std::sync::atomic::AtomicU64 = std::sync::atomic::Atom
/// An address as a trace may keep it: without its query or user and password, where a private
/// feed carries its credential (Patreon's `?auth=`), which Tempo kept for anyone reading it (#225).
/// shortcut: a token in the path (`/feed/<token>.xml`) stays; strip paths too if a feed does that.
#[must_use]
pub fn redact(url: &str) -> String {
match url::Url::parse(url) {
Ok(mut u) => {
@@ -103,6 +104,7 @@ pub fn redact(url: &str) -> String {
/// Text with each address in it redacted, for what is logged and sent to every open page: an
/// error from reqwest names the whole address it fetched, a private feed's key with it (#238).
#[must_use]
pub fn redact_text(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut rest = text;
@@ -263,6 +265,7 @@ pub struct Failure {
/// A failure's kind, for the log's `error.type` (#91): the HTTP status where there is one, as
/// OpenTelemetry names an HTTP error, and otherwise a word for what went wrong. Matches the
/// same wording as `explain_failure`; a message it does not know is "other", never a wrong kind.
#[must_use]
pub fn failure_kind(msg: &str) -> (String, Option<u16>) {
let low = msg.to_ascii_lowercase();
let code = low.split("http ").skip(1).find_map(|r| r.get(..3)?.parse::<u16>().ok());
@@ -344,6 +347,7 @@ pub fn explain_failure(msg: &str) -> Option<Failure> {
/// "Temporary failure in name resolution", musl's "Try again" -- as against a name that does
/// not exist ("Name or service not known"). It says the network was not answering, not that the
/// feed is gone (#143).
#[must_use]
pub fn is_temporary_lookup_failure(msg: &str) -> bool {
let low = msg.to_ascii_lowercase();
low.contains("dns error") && (low.contains("temporary failure in name resolution") || low.contains("try again"))
@@ -353,6 +357,7 @@ pub fn is_temporary_lookup_failure(msg: &str) -> bool {
///
/// The original matched on the URL ending in ".opml" (iPXClass.py:34), which misses an
/// OPML served from a URL without that extension. Sniffing the body catches both.
#[must_use]
pub fn is_opml(bytes: &[u8]) -> bool {
let head = &bytes[..bytes.len().min(1024)];
let text = String::from_utf8_lossy(head).to_lowercase();
@@ -407,6 +412,7 @@ pub fn collect_outlines(outlines: &[opml::Outline], out: &mut Vec<(String, Strin
}
/// The <head><title> of an OPML document.
#[must_use]
pub fn opml_title(bytes: &[u8]) -> Option<String> {
let text = String::from_utf8_lossy(bytes);
let doc = read_opml(&text).ok()?;
@@ -427,6 +433,7 @@ fn patreon_parts(url: &str) -> Option<(String, Option<String>)> {
}
/// A Patreon link naming a creator but no show.
#[must_use]
pub fn is_patreon_creator(url: &str) -> bool {
matches!(patreon_parts(url), Some((_, None)))
}
@@ -434,6 +441,7 @@ pub fn is_patreon_creator(url: &str) -> bool {
/// What was typed into Add feed, as a URL. A bare Patreon token is taken as its creator's
/// feed, since the token alone says whose it is. An address with no scheme is https: 'cnn.com'
/// was stored as typed and every check failed with "relative URL without a base" (#101).
#[must_use]
pub fn expand_input(input: &str) -> String {
let s = input.trim();
let token = s.len() >= 20 && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_');
@@ -451,6 +459,7 @@ pub fn expand_input(input: &str) -> String {
/// Whether two URLs are the same feed. One Patreon show has several spellings -- by the
/// creator's name, by number, or with no creator at all -- and the token and show are what
/// identify it.
#[must_use]
pub fn same_feed(a: &str, b: &str) -> bool {
// Parsing both is the slow part, and only Patreon's addresses can differ and still match.
a == b
@@ -1019,6 +1028,7 @@ fn drop_player_repeats(encs: &mut Vec<Enclosure>) {
/// An enclosure URL without WordPress's player number, for telling repeats of one file apart
/// from different files.
#[must_use]
pub fn same_file_key(url: &str) -> String {
let Ok(mut u) = url::Url::parse(url) else { return url.to_owned() };
let kept: Vec<(String, String)> = u
@@ -1095,6 +1105,7 @@ fn is_markdown(s: &str) -> bool {
}
/// Markdown made HTML, uncleaned: whoever shows it cleans it as any feed's HTML.
#[must_use]
pub fn markdown_html(md: &str) -> String {
use pulldown_cmark::{Options, Parser, html};
let mut out = String::new();
@@ -1302,10 +1313,12 @@ const ALIASES: &[(&str, &str)] =
/// category and then its subcategories as Apple's does (#118). A podcast's own is stored as its
/// subcategory, and one an admin typed that is not Apple's stands as a category of its own.
/// Apple's categories as ipx has them, for the directory to check against Apple's own (#252).
#[must_use]
pub fn categories() -> &'static [(&'static str, &'static [&'static str])] {
CATEGORIES
}
#[must_use]
pub fn category_path(name: &str) -> (String, Option<String>) {
// A feed's own text, which can end in a stray comma: "Soccer," was a category of its own.
let name = name.trim().trim_end_matches([',', ';', '.']).trim_end();
@@ -1376,6 +1389,7 @@ fn picked(answer: &serde_json::Value) -> Result<String> {
///
/// ponytail: a heuristic. A key hidden in the path of a host not in `PAID_HOSTS` gets
/// through; a per-feed private flag is the upgrade (#205).
#[must_use]
pub fn url_looks_private(feed_url: &str) -> bool {
let Ok(u) = url::Url::parse(feed_url) else { return true };
// Paid-feed services put the subscriber's key in the path as often as in the query, and a