Skip to main content

max / makenotwork

livechat: adopt universal clippy/lint baseline; warnings to zero Renamed the misleadingly-underscored _permit field to permit (used_underscore_binding), by-value self on the Copy RateLimits method (trivially_copy_pass_by_ref), bound the error in a test panic (match_wild_err_arm). clippy --all-targets clean, cargo fmt clean, tests green (57 passed+3 passed+2 passed+0 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-23 13:14 UTC
Commit: c248ab54eb183472388b8909290cdb237de9b21c
Parent: 994c53b
5 files changed, +49 insertions, -19 deletions
@@ -26,3 +26,35 @@ tracing = { version = "0.1", optional = true }
26 26 [dev-dependencies]
27 27 serde_json = "1"
28 28 tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread", "sync", "time"] }
29 +
30 + [lints.rust]
31 + unused = "warn"
32 + unreachable_pub = "warn"
33 +
34 + [lints.clippy]
35 + pedantic = { level = "warn", priority = -1 }
36 + # Allow-list tuned from a measured breakdown across server/multithreaded/pter
37 + # (2026-07-22). These are the high-churn / low-signal pedantic lints; everything
38 + # else in `pedantic` stays a warning. Keep this block identical across repos.
39 + module_name_repetitions = "allow"
40 + # Doc lints — no docs-completeness push underway.
41 + missing_errors_doc = "allow"
42 + missing_panics_doc = "allow"
43 + doc_markdown = "allow"
44 + # Numeric casts — endemic and mostly intentional in size/byte math.
45 + cast_possible_truncation = "allow"
46 + cast_sign_loss = "allow"
47 + cast_precision_loss = "allow"
48 + cast_possible_wrap = "allow"
49 + cast_lossless = "allow"
50 + # Subjective structure/style nags — high churn, low signal.
51 + must_use_candidate = "allow"
52 + too_many_lines = "allow"
53 + struct_excessive_bools = "allow"
54 + similar_names = "allow"
55 + items_after_statements = "allow"
56 + single_match_else = "allow"
57 + # Frequent false-positives in TUI/router-heavy code — added from the buckets breakdown.
58 + match_same_arms = "allow"
59 + unnecessary_wraps = "allow"
60 + type_complexity = "allow"
@@ -129,11 +129,7 @@ impl Hub {
129 129 .or_insert_with(|| broadcast::channel(self.inner.limits.room_buffer).0)
130 130 .subscribe();
131 131
132 - Ok(Subscription {
133 - rx,
134 - room,
135 - _permit: permit,
136 - })
132 + Ok(Subscription { rx, room, permit })
137 133 }
138 134
139 135 /// Rooms currently holding a channel. Diagnostics only.
@@ -152,8 +148,7 @@ impl Hub {
152 148 self.inner
153 149 .rooms
154 150 .get(&room)
155 - .map(|tx| tx.receiver_count())
156 - .unwrap_or(0)
151 + .map_or(0, |tx| tx.receiver_count())
157 152 }
158 153 }
159 154
@@ -223,7 +218,7 @@ impl Drop for ConnectionPermit {
223 218 pub struct Subscription {
224 219 rx: broadcast::Receiver<ChatEvent>,
225 220 room: RoomId,
226 - _permit: ConnectionPermit,
221 + permit: ConnectionPermit,
227 222 }
228 223
229 224 impl Subscription {
@@ -255,7 +250,7 @@ impl Drop for Subscription {
255 250 // this instant either keeps this channel or creates a fresh one after the
256 251 // removal. It cannot end up holding a receiver on a sender that has just
257 252 // been dropped from the map, which is the silent-dead-room failure.
258 - self._permit
253 + self.permit
259 254 .inner
260 255 .rooms
261 256 .remove_if(&self.room, |_, tx| tx.receiver_count() <= 1);
@@ -48,7 +48,7 @@ impl Default for RateLimits {
48 48 }
49 49
50 50 impl RateLimits {
51 - fn refill_per_sec(&self) -> f64 {
51 + fn refill_per_sec(self) -> f64 {
52 52 f64::from(self.sustain_per_min) / 60.0
53 53 }
54 54 }
@@ -220,7 +220,7 @@ mod tests {
220 220 let (u, r, t0) = (user(), room(), Instant::now());
221 221
222 222 assert!(rl.check(u, r, t0).is_allowed());
223 - let long_later = t0 + Duration::from_secs(3600);
223 + let long_later = t0 + Duration::from_hours(1);
224 224
225 225 for i in 0..3 {
226 226 assert!(rl.check(u, r, long_later).is_allowed(), "message {i}");
@@ -271,7 +271,10 @@ mod tests {
271 271 assert!(rl.check(u, r, t).is_allowed());
272 272 // Earlier than the bucket's last touch: elapsed saturates to zero rather
273 273 // than underflowing.
274 - assert!(rl.check(u, r, t - Duration::from_secs(5)).is_allowed());
274 + assert!(
275 + rl.check(u, r, t.checked_sub(Duration::from_secs(5)).unwrap())
276 + .is_allowed()
277 + );
275 278 }
276 279
277 280 #[test]
@@ -285,7 +288,7 @@ mod tests {
285 288 rl.check(idle, r, t0);
286 289 assert_eq!(rl.bucket_count(), 2);
287 290
288 - let later = t0 + Duration::from_secs(60);
291 + let later = t0 + Duration::from_mins(1);
289 292 rl.check(busy, r, later);
290 293
291 294 assert_eq!(rl.evict_idle(later, Duration::from_secs(30)), 1);
@@ -15,7 +15,7 @@ use std::time::{Duration, SystemTime};
15 15 use crate::error::ChatError;
16 16
17 17 /// Longest retention any room may request.
18 - pub const MAX_AGE_CEILING: Duration = Duration::from_secs(30 * 24 * 60 * 60);
18 + pub const MAX_AGE_CEILING: Duration = Duration::from_hours(720);
19 19
20 20 /// Most messages any room may hold.
21 21 pub const MAX_MESSAGES_CEILING: usize = 20_000;
@@ -61,7 +61,7 @@ impl Retention {
61 61 /// messages.
62 62 pub fn forum_default() -> Self {
63 63 Self {
64 - max_age: Duration::from_secs(7 * 24 * 60 * 60),
64 + max_age: Duration::from_hours(168),
65 65 max_messages: 5_000,
66 66 }
67 67 }
@@ -70,7 +70,7 @@ impl Retention {
70 70 /// messages. A creator may raise it, still bounded by the ceilings.
71 71 pub fn stream_default() -> Self {
72 72 Self {
73 - max_age: Duration::from_secs(24 * 60 * 60),
73 + max_age: Duration::from_hours(24),
74 74 max_messages: 5_000,
75 75 }
76 76 }
@@ -105,7 +105,7 @@ mod tests {
105 105 Err(ChatError::RetentionTooLong { .. })
106 106 ));
107 107 assert!(matches!(
108 - Retention::new(Duration::from_secs(60), MAX_MESSAGES_CEILING + 1),
108 + Retention::new(Duration::from_mins(1), MAX_MESSAGES_CEILING + 1),
109 109 Err(ChatError::RetentionTooLong { .. })
110 110 ));
111 111 }
@@ -122,7 +122,7 @@ mod tests {
122 122 Err(ChatError::RetentionTooShort)
123 123 ));
124 124 assert!(matches!(
125 - Retention::new(Duration::from_secs(60), 0),
125 + Retention::new(Duration::from_mins(1), 0),
126 126 Err(ChatError::RetentionTooShort)
127 127 ));
128 128 }
@@ -169,7 +169,7 @@ async fn a_stable_listener_receives_everything_through_churn() {
169 169 Ok(Some(ChatEvent::Gap)) => panic!("buffer was sized to make a gap impossible here"),
170 170 Ok(Some(_)) => received += 1,
171 171 Ok(None) => panic!("stable listener's room was closed out from under it"),
172 - Err(_) => panic!("stable listener stopped receiving after {received} events"),
172 + Err(e) => panic!("stable listener stopped receiving after {received} events: {e:?}"),
173 173 }
174 174 }
175 175