Skip to main content

max / makenotwork

multithreaded: adopt lint block, fix clippy, fmt Green under 'SQLX_OFFLINE=true cargo clippy --all-targets -- -D warnings': explicit imports over wildcard, write! over format!-push, let-else, pass Copy CommunityRole by value, #[must_use], Path-based ext checks. Scoped #[allow] (with reason) only on the two db_error .map_err helpers. C1 disallowed_methods seal untouched; no authz/SQL semantics changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-24 15:06 UTC
Commit: 7a2b0eca2df12c7eee33ef540e0da8b25defb88f
Parent: 0c85519
106 files changed, +716 insertions, -862 deletions
@@ -107,3 +107,38 @@ pom-contract = { path = "../shared/pom-contract" }
107 107 # Enable mt-db's setup-only mutations for the integration suite. Resolver 2 keeps
108 108 # this feature out of the normal/production build.
109 109 mt-db = { workspace = true, features = ["test-support"] }
110 +
111 + [workspace.lints.rust]
112 + unused = "warn"
113 + unreachable_pub = "warn"
114 +
115 + [workspace.lints.clippy]
116 + pedantic = { level = "warn", priority = -1 }
117 + # Allow-list tuned from a measured breakdown across server/multithreaded/pter
118 + # (2026-07-22). These are the high-churn / low-signal pedantic lints; everything
119 + # else in `pedantic` stays a warning. Keep this block identical across repos.
120 + module_name_repetitions = "allow"
121 + # Doc lints. No docs-completeness push is underway.
122 + missing_errors_doc = "allow"
123 + missing_panics_doc = "allow"
124 + doc_markdown = "allow"
125 + # Numeric casts. Endemic and mostly intentional in size and byte math.
126 + cast_possible_truncation = "allow"
127 + cast_sign_loss = "allow"
128 + cast_precision_loss = "allow"
129 + cast_possible_wrap = "allow"
130 + cast_lossless = "allow"
131 + # Subjective structure and style nags. High churn, low signal.
132 + must_use_candidate = "allow"
133 + too_many_lines = "allow"
134 + struct_excessive_bools = "allow"
135 + similar_names = "allow"
136 + items_after_statements = "allow"
137 + single_match_else = "allow"
138 + # Frequent false-positives in TUI and router-heavy code.
139 + match_same_arms = "allow"
140 + unnecessary_wraps = "allow"
141 + type_complexity = "allow"
142 +
143 + [lints]
144 + workspace = true
@@ -18,7 +18,7 @@ fn main() {
18 18 /// One global version rather than a per-file hash: an upgrade to any watched
19 19 /// file re-fetches all of them, which costs a handful of requests once and
20 20 /// keeps the templates free of per-asset bookkeeping. What it buys is that a
21 - /// redeployed file can never be served stale from browser cache — for a chat
21 + /// redeployed file can never be served stale from browser cache, for a chat
22 22 /// island that would mean old protocol logic talking to a new server, which
23 23 /// presents as "chat is broken for some people" rather than as a cache bug.
24 24 ///
@@ -66,9 +66,7 @@ fn fingerprint_assets() {
66 66
67 67 /// Write `contents` to `path` only if it differs, to avoid needless rebuilds.
68 68 fn write_if_changed(path: &Path, contents: &str) {
69 - let needs_write = fs::read_to_string(path)
70 - .map(|existing| existing != contents)
71 - .unwrap_or(true);
69 + let needs_write = fs::read_to_string(path).map_or(true, |existing| existing != contents);
72 70 if needs_write {
73 71 fs::write(path, contents)
74 72 .unwrap_or_else(|e| panic!("failed to write {}: {e}", path.display()));
@@ -76,7 +74,7 @@ fn write_if_changed(path: &Path, contents: &str) {
76 74 }
77 75
78 76 /// Recursively hash every `.js` file under `dir` into `hasher`, in a
79 - /// deterministic order. A missing directory is a no-op — the first build on a
77 + /// deterministic order. A missing directory is a no-op, the first build on a
80 78 /// fresh checkout runs before the frontend has been compiled.
81 79 fn hash_dir_js(dir: &Path, hasher: &mut DefaultHasher) {
82 80 let Ok(entries) = fs::read_dir(dir) else {
@@ -7,3 +7,6 @@ edition.workspace = true
7 7 chrono = { workspace = true }
8 8 serde = { workspace = true }
9 9 sqlx = { workspace = true }
10 +
11 + [lints]
12 + workspace = true
@@ -4,9 +4,7 @@ use serde::Serialize;
4 4 use sqlx::types::Uuid;
5 5 use std::fmt;
6 6
7 - // ============================================================================
8 - // CommunityRole — owner, moderator, member
9 - // ============================================================================
7 + // CommunityRole, owner, moderator, member
10 8
11 9 /// Role a user holds within a community.
12 10 #[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type)]
@@ -58,9 +56,7 @@ impl fmt::Display for CommunityRole {
58 56 }
59 57 }
60 58
61 - // ============================================================================
62 - // CommunityState — moderation state machine
63 - // ============================================================================
59 + // CommunityState, moderation state machine
64 60
65 61 /// Community-level moderation state. Distinct from platform suspension
66 62 /// (`communities.suspended_at`); this is what owners/mods control.
@@ -130,9 +126,7 @@ impl fmt::Display for CommunityState {
130 126 }
131 127 }
132 128
133 - // ============================================================================
134 - // BanType — ban or mute
135 - // ============================================================================
129 + // BanType, ban or mute
136 130
137 131 /// Type of community restriction.
138 132 #[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type)]
@@ -157,9 +151,7 @@ impl fmt::Display for BanType {
157 151 }
158 152 }
159 153
160 - // ============================================================================
161 - // ModAction — actions recorded in the mod log
162 - // ============================================================================
154 + // ModAction, actions recorded in the mod log
163 155
164 156 /// Action recorded in the moderation log.
165 157 #[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type)]
@@ -221,7 +213,7 @@ impl ModAction {
221 213 /// Who performed a moderation action, for attribution in the mod log.
222 214 ///
223 215 /// A `System` action (e.g. flag-threshold auto-hide) is recorded with a NULL
224 - /// `actor_id`, mirroring the `posts.removed_by IS NULL` convention — never the
216 + /// `actor_id`, mirroring the `posts.removed_by IS NULL` convention, never the
225 217 /// user who happened to trip the threshold. `User` carries the moderator's
226 218 /// `mnw_account_id`.
227 219 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -246,9 +238,7 @@ impl fmt::Display for ModAction {
246 238 }
247 239 }
248 240
249 - // ============================================================================
250 - // SortOrder — ascending or descending
251 - // ============================================================================
241 + // SortOrder, ascending or descending
252 242
253 243 /// Sort direction for paginated listings.
254 244 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -280,9 +270,7 @@ impl fmt::Display for SortOrder {
280 270 }
281 271 }
282 272
283 - // ============================================================================
284 - // SortColumn — which column to sort threads by
285 - // ============================================================================
273 + // SortColumn, which column to sort threads by
286 274
287 275 /// Column used to sort thread listings.
288 276 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -15,3 +15,6 @@ chrono = { workspace = true }
15 15 uuid = { workspace = true }
16 16 tracing = { workspace = true }
17 17 serde = { workspace = true }
18 +
19 + [lints]
20 + workspace = true
@@ -1,4 +1,4 @@
1 - //! Database access layer — queries and mutations for Multithreaded.
1 + //! Database access layer: queries and mutations for Multithreaded.
2 2
3 3 pub mod mutations;
4 4 pub mod queries;
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Create a new category in a community.
4 4 #[tracing::instrument(skip_all)]
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{CommunityState, PgPool, Utc, Uuid};
2 2
3 3 /// Create a new community and return its ID.
4 4 #[tracing::instrument(skip_all)]
@@ -96,7 +96,7 @@ pub struct CleanSlateResult {
96 96 /// then inserted). Useful for the success toast.
97 97 pub deleted_thread_count: i64,
98 98 /// ID of the system "Community reset" thread created in the first
99 - /// category. `None` if the community has no categories — clean-slate
99 + /// category. `None` if the community has no categories, clean-slate
100 100 /// still deletes threads but has nowhere to post the notice.
101 101 pub system_thread_id: Option<Uuid>,
102 102 }
@@ -132,7 +132,7 @@ pub async fn clean_slate_community(
132 132 .await?;
133 133
134 134 // Pick the first category by sort_order to host the reset notice. None
135 - // means the community has no categories — nothing to post into.
135 + // means the community has no categories, nothing to post into.
136 136 let first_category = sqlx::query_scalar!(
137 137 "SELECT id FROM categories
138 138 WHERE community_id = $1
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Toggle endorsement: insert if missing, delete if exists. Returns true if now endorsed.
4 4 /// Uses a transaction to prevent race conditions between concurrent toggle requests.
@@ -19,7 +19,7 @@ pub async fn toggle_endorsement(
19 19 .await?;
20 20
21 21 if result.rows_affected() == 0 {
22 - // Already existed — remove it
22 + // Already existed; remove it
23 23 sqlx::query!(
24 24 "DELETE FROM post_endorsements WHERE post_id = $1 AND endorser_id = $2",
25 25 post_id,
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Insert a footnote on a post. Returns the footnote ID.
4 4 #[tracing::instrument(skip_all)]
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Insert an uploaded image record.
4 4 #[tracing::instrument(skip_all)]
@@ -44,8 +44,8 @@ pub async fn remove_image<'e, E: sqlx::PgExecutor<'e>>(
44 44 }
45 45
46 46 /// Hard-delete an image row. Used to roll back the row inserted just before a
47 - /// failed S3 upload — the object never landed in the bucket, so there is
48 - /// nothing to reconcile and the row should simply not exist.
47 + /// failed S3 upload, the object never landed in the bucket, so there is
48 + /// nothing to reconcile and the row should not exist.
49 49 #[tracing::instrument(skip_all)]
50 50 pub async fn delete_image_row(pool: &PgPool, image_id: Uuid) -> Result<(), sqlx::Error> {
51 51 sqlx::query!("DELETE FROM images WHERE id = $1", image_id)
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Insert a link preview for a post. Ignores duplicates.
4 4 #[tracing::instrument(skip_all)]
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{CommunityRole, PgPool, Uuid};
2 2
3 3 /// Ensure a user has a membership in a community with the given role.
4 4 /// Creates membership if none exists, does nothing if already a member.
@@ -1,4 +1,6 @@
1 - use super::*;
1 + use std::fmt::Write as _;
2 +
3 + use super::Uuid;
2 4
3 5 /// Insert mention rows for a post (batch insert). Ignores duplicates.
4 6 #[tracing::instrument(skip_all)]
@@ -17,7 +19,7 @@ pub async fn insert_mentions<'e, E: sqlx::PgExecutor<'e>>(
17 19 if i > 0 {
18 20 sql.push_str(", ");
19 21 }
20 - sql.push_str(&format!("($1, ${})", i + 2));
22 + let _ = write!(sql, "($1, ${})", i + 2);
21 23 }
22 24 sql.push_str(" ON CONFLICT DO NOTHING");
23 25 let mut query = sqlx::query(&sql).bind(post_id);
@@ -1,4 +1,4 @@
1 - //! Database write mutations — inserts, updates, deletes.
1 + //! Database write mutations, inserts, updates, deletes.
2 2
3 3 use chrono::{DateTime, Utc};
4 4 use mt_core::types::{BanType, CommunityRole, CommunityState, ModAction, ModActor};
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{BanType, DateTime, ModAction, ModActor, PgPool, Utc, Uuid};
2 2
3 3 /// Create or update a ban/mute. Returns the ban ID.
4 4 #[tracing::instrument(skip_all)]
@@ -71,7 +71,7 @@ pub async fn remove_community_ban<'e, E: sqlx::PgExecutor<'e>>(
71 71 /// Insert a mod log entry on the given executor.
72 72 ///
73 73 /// Generic over the executor so the audit row can be written on the *same*
74 - /// transaction as the mutation it records — handlers open one `tx`, run the
74 + /// transaction as the mutation it records: handlers open one `tx`, run the
75 75 /// mutation and this insert on `&mut *tx`, then commit, so an auditable action
76 76 /// can never commit without its log row. A `System` actor persists as a NULL
77 77 /// `actor_id` (migration 032).
@@ -1,11 +1,11 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Insert a reply and bump the thread's last_activity_at atomically.
4 4 ///
5 5 /// Opening posts are created with the thread via [`create_thread_with_op`];
6 6 /// this is for replies only. The display reply count derives from the
7 7 /// denormalized `threads.post_count`, which the m029 trigger maintains on every
8 - /// post INSERT/DELETE/removed_at change — no counter to update by hand here.
8 + /// post INSERT/DELETE/removed_at change, no counter to update by hand here.
9 9 #[tracing::instrument(skip_all)]
10 10 pub async fn create_post(
11 11 pool: &PgPool,
@@ -40,7 +40,7 @@ pub async fn create_post_tx(
40 40 body_html: &str,
41 41 ) -> Result<Option<Uuid>, sqlx::Error> {
42 42 // Insert only while the thread is still live and unlocked, in the same
43 - // statement that reads that state — closing the TOCTOU between a handler's
43 + // statement that reads that state, closing the TOCTOU between a handler's
44 44 // snapshot-time `locked`/`deleted_at` check and this insert. Without the
45 45 // guard a concurrent lock or soft-delete in the gap would let the reply
46 46 // commit (and the m029 post_count trigger increment a deleted thread). None
@@ -81,7 +81,7 @@ pub async fn create_post_tx(
81 81 /// server→server reply carrying the same `external_ref` inserts at most once.
82 82 /// `ON CONFLICT` on the partial unique index (m030) makes the repeat a no-op,
83 83 /// so the m029 post_count trigger fires exactly once and `last_activity_at` is
84 - /// bumped only on the fresh insert. Returns `(post_id, created)` — `created` is
84 + /// bumped only on the fresh insert. Returns `(post_id, created)`, `created` is
85 85 /// false when an existing post was returned. User-facing replies use the plain
86 86 /// [`create_post`] (no ref); this path is internal-only.
87 87 #[tracing::instrument(skip_all)]
@@ -137,7 +137,7 @@ pub async fn create_post_external_ref(
137 137 /// Test-only: mod-remove a single post without the OP-cascade.
138 138 ///
139 139 /// Production code must use [`mod_remove_post_cascade`], which also soft-deletes
140 - /// the thread when the post is its opening post — removing an OP without the
140 + /// the thread when the post is its opening post, removing an OP without the
141 141 /// cascade leaves a headless, still-repliable thread. This non-cascade variant
142 142 /// exists solely so the integration suite can stage a removed post directly; it
143 143 /// is gated behind the `test-support` feature so handler code cannot reach it.
@@ -177,7 +177,7 @@ pub struct PostRemoval {
177 177 /// Removing an OP without this cascade would leave a headless, still-repliable
178 178 /// thread; cascading deletes it so listings, the thread view, and the reply
179 179 /// path all treat it as gone. Returns false flags when the post was already
180 - /// removed or was not the OP — callers can log a thread-deletion accordingly.
180 + /// removed or was not the OP, callers can log a thread-deletion accordingly.
181 181 #[tracing::instrument(skip_all)]
182 182 pub async fn mod_remove_post_cascade(
183 183 conn: &mut sqlx::PgConnection,
@@ -1,4 +1,6 @@
1 - use super::*;
1 + use std::fmt::Write as _;
2 +
3 + use super::{PgPool, Uuid};
2 4
3 5 /// Create a tag in a community. Returns the tag ID.
4 6 #[tracing::instrument(skip_all)]
@@ -69,7 +71,7 @@ pub async fn set_thread_tags_tx(
69 71 sql.push_str(", ");
70 72 }
71 73 // $1 = thread_id, $2.. = tag_ids
72 - sql.push_str(&format!("($1, ${})", i + 2));
74 + let _ = write!(sql, "($1, ${})", i + 2);
73 75 }
74 76 let mut query = sqlx::query(&sql).bind(thread_id);
75 77 for tag_id in tag_ids {
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Atomically create an externally-referenced thread and its opening post in
4 4 /// one transaction. Returns `(thread_id, op_post_id)`.
@@ -48,7 +48,7 @@ pub async fn create_thread_with_op_external_ref(
48 48
49 49 /// Low-level: insert a bare thread row (no opening post). Returns the thread ID.
50 50 ///
51 - /// Handlers must NOT pair this with a separate `create_post` for the OP — that
51 + /// Handlers must NOT pair this with a separate `create_post` for the OP, that
52 52 /// non-atomic sequence can leak a post-less thread. Use [`create_thread_with_op`]
53 53 /// for the real "start a thread" operation. This primitive exists for tests and
54 54 /// data construction that build posts explicitly.
@@ -143,7 +143,7 @@ pub async fn create_thread_with_op_tx(
143 143 /// Atomically auto-hide a post if pending flag count meets the threshold.
144 144 /// Combines count check and removal in a single query to avoid race conditions.
145 145 /// Returns true if the post was actually removed.
146 - /// Sets removed_by to NULL (system action) — the mod log records the event.
146 + /// Sets removed_by to NULL (system action), the mod log records the event.
147 147 #[tracing::instrument(skip_all)]
148 148 pub async fn auto_hide_if_threshold_met<'e, E: sqlx::PgExecutor<'e>>(
149 149 executor: E,
@@ -1,4 +1,4 @@
1 - use super::*;
1 + use super::{PgPool, Uuid};
2 2
3 3 /// Track a thread (upsert).
4 4 #[tracing::instrument(skip_all)]