Skip to main content

max / makenotwork

sando: adopt lint block, fix clippy, fmt Green under 'cargo clippy --all-targets -- -D warnings': elide needless lifetimes on sqlx Encode impls, Path-based extension checks, heap-box a large buffer, let-else, #[must_use], display PathBuf. No suppressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-24 15:01 UTC
Commit: 0c85519c3ff6639f2dbd0ffd5132205e1b966366
Parent: 28eedb8
17 files changed, +102 insertions, -71 deletions
@@ -5,3 +5,35 @@
5 5 [workspace]
6 6 resolver = "3"
7 7 members = ["daemon"]
8 +
9 + [workspace.lints.rust]
10 + unused = "warn"
11 + unreachable_pub = "warn"
12 +
13 + [workspace.lints.clippy]
14 + pedantic = { level = "warn", priority = -1 }
15 + # Allow-list tuned from a measured breakdown across server/multithreaded/pter
16 + # (2026-07-22). These are the high-churn / low-signal pedantic lints; everything
17 + # else in `pedantic` stays a warning. Keep this block identical across repos.
18 + module_name_repetitions = "allow"
19 + # Doc lints. No docs-completeness push is underway.
20 + missing_errors_doc = "allow"
21 + missing_panics_doc = "allow"
22 + doc_markdown = "allow"
23 + # Numeric casts. Endemic and mostly intentional in size and byte math.
24 + cast_possible_truncation = "allow"
25 + cast_sign_loss = "allow"
26 + cast_precision_loss = "allow"
27 + cast_possible_wrap = "allow"
28 + cast_lossless = "allow"
29 + # Subjective structure and style nags. High churn, low signal.
30 + must_use_candidate = "allow"
31 + too_many_lines = "allow"
32 + struct_excessive_bools = "allow"
33 + similar_names = "allow"
34 + items_after_statements = "allow"
35 + single_match_else = "allow"
36 + # Frequent false-positives in TUI and router-heavy code.
37 + match_same_arms = "allow"
38 + unnecessary_wraps = "allow"
39 + type_complexity = "allow"
@@ -32,3 +32,6 @@ tempfile = "3.20"
32 32 tower = { version = "0.5", features = ["util"] }
33 33 http-body-util = "0.1"
34 34 reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"] }
35 +
36 + [lints]
37 + workspace = true
@@ -75,7 +75,7 @@ pub(crate) fn parse_source(s: &str) -> Result<BackupSource> {
75 75 None => (user_host_port.to_string(), None),
76 76 };
77 77 if user_host.is_empty() {
78 - bail!("ssh:// URL has empty host (port {:?})", port);
78 + bail!("ssh:// URL has empty host (port {port:?})");
79 79 }
80 80 return Ok(BackupSource::Ssh {
81 81 user_host,
@@ -147,7 +147,9 @@ pub async fn fetch(
147 147 // restores (CF4). `--inplace`/`--partial` are deliberately NOT used — those
148 148 // keep a truncated file on failure, the opposite of what we want here.
149 149 let tmp_path = format!("{local_path}.partial");
150 - let is_gz = local_path.ends_with(".gz");
150 + let is_gz = std::path::Path::new(&local_path)
151 + .extension()
152 + .is_some_and(|ext| ext.eq_ignore_ascii_case("gz"));
151 153
152 154 // Plausibility floor: half the last verified backup's size, never below the
153 155 // absolute floor. The first-ever fetch (no prior row) falls back to the
@@ -156,9 +158,9 @@ pub async fn fetch(
156 158 sqlx::query_scalar("SELECT byte_size FROM backups ORDER BY fetched_at DESC LIMIT 1")
157 159 .fetch_optional(pool)
158 160 .await?;
159 - let min_bytes = last_size
160 - .map(|s| ((s / MIN_BACKUP_FRACTION_DENOM) as u64).max(MIN_BACKUP_BYTES))
161 - .unwrap_or(MIN_BACKUP_BYTES);
161 + let min_bytes = last_size.map_or(MIN_BACKUP_BYTES, |s| {
162 + ((s / MIN_BACKUP_FRACTION_DENOM) as u64).max(MIN_BACKUP_BYTES)
163 + });
162 164
163 165 let parsed = parse_source(&source)?;
164 166 let downloaded: Result<()> = async {
@@ -183,8 +183,7 @@ pub async fn run(
183 183 let release_dir = cfg
184 184 .cargo_target_dir
185 185 .as_deref()
186 - .map(|t| t.join("release"))
187 - .unwrap_or_else(|| server_dir.join("target/release"));
186 + .map_or_else(|| server_dir.join("target/release"), |t| t.join("release"));
188 187 let mut binary_paths = Vec::with_capacity(cfg.bin_names.len());
189 188 for name in &cfg.bin_names {
190 189 let p = release_dir.join(name);
@@ -117,7 +117,7 @@ fn hash_file(path: &Path) -> std::io::Result<String> {
117 117 use std::io::Read;
118 118 let mut file = std::fs::File::open(path)?;
119 119 let mut hasher = Sha256::new();
120 - let mut buf = [0u8; 64 * 1024];
120 + let mut buf = vec![0u8; 64 * 1024].into_boxed_slice();
121 121 loop {
122 122 let n = file.read(&mut buf)?;
123 123 if n == 0 {
@@ -162,7 +162,7 @@ fn parse_due_to_count(s: &str) -> Option<u32> {
162 162 let idx = s.find("due to ")?;
163 163 let digits: String = s[idx + 7..]
164 164 .chars()
165 - .take_while(|c| c.is_ascii_digit())
165 + .take_while(char::is_ascii_digit)
166 166 .collect();
167 167 digits.parse().ok()
168 168 }
@@ -189,9 +189,7 @@ pub fn classify_migration_error(err: &str, migration_hint: Option<&str>) -> Gate
189 189 return GateFailure::MigrationModified { migration: m };
190 190 }
191 191 let sqlstate = extract_sqlstate(err);
192 - let migration = migration_hint
193 - .map(str::to_owned)
194 - .unwrap_or_else(|| "?".to_owned());
192 + let migration = migration_hint.map_or_else(|| "?".to_owned(), str::to_owned);
195 193 if sqlstate.is_some() {
196 194 return GateFailure::MigrationSqlError {
197 195 migration,
@@ -207,14 +205,14 @@ fn extract_drift(err: &str) -> Option<String> {
207 205 // "migration N was previously applied but is missing in the resolved migrations"
208 206 let idx = err.find(" was previously applied but is missing")?;
209 207 let prefix = &err[..idx];
210 - let mig = prefix.rsplit_once(' ').map(|(_, m)| m).unwrap_or(prefix);
208 + let mig = prefix.rsplit_once(' ').map_or(prefix, |(_, m)| m);
211 209 Some(mig.to_string())
212 210 }
213 211
214 212 fn extract_modified(err: &str) -> Option<String> {
215 213 let idx = err.find(" was previously applied but has been modified")?;
216 214 let prefix = &err[..idx];
217 - let mig = prefix.rsplit_once(' ').map(|(_, m)| m).unwrap_or(prefix);
215 + let mig = prefix.rsplit_once(' ').map_or(prefix, |(_, m)| m);
218 216 Some(mig.to_string())
219 217 }
220 218
@@ -170,8 +170,7 @@ fn scratch_db_host(url: &str) -> Option<String> {
170 170 let after = url.find("://").map(|i| i + 3)?;
171 171 let authority_end = url[after..]
172 172 .find(['/', '?', '#'])
173 - .map(|i| after + i)
174 - .unwrap_or(url.len());
173 + .map_or(url.len(), |i| after + i);
175 174 let authority = &url[after..authority_end];
176 175 // Drop userinfo: keep everything after the last '@' (`user:pass@host` → `host`).
177 176 let host_port = authority.rsplit('@').next().unwrap_or(authority);
@@ -256,8 +255,7 @@ impl Config {
256 255 pub fn primary_bin(&self) -> &str {
257 256 self.bin_names
258 257 .first()
259 - .map(|s| s.as_str())
260 - .unwrap_or("server")
258 + .map_or("server", std::string::String::as_str)
261 259 }
262 260
263 261 pub fn load() -> Result<Self> {
@@ -291,8 +289,8 @@ impl Config {
291 289 for t in &self.test_targets {
292 290 anyhow::ensure!(
293 291 !t.all_features || t.features.is_empty(),
294 - "test_target {:?} sets both all_features and features; pick one",
295 - t.dir,
292 + "test_target {} sets both all_features and features; pick one",
293 + t.dir.display(),
296 294 );
297 295 }
298 296 if let Some(url) = self.scratch_db_url.as_deref() {
@@ -64,8 +64,7 @@ async fn run_checked(executor: &dyn Executor, script: &str, what: &str) -> Resul
64 64 "{what} failed (exit {}): {}",
65 65 out.status
66 66 .code()
67 - .map(|c| c.to_string())
68 - .unwrap_or_else(|| "signal".into()),
67 + .map_or_else(|| "signal".into(), |c| c.to_string()),
69 68 String::from_utf8_lossy(&out.stderr),
70 69 );
71 70 Ok(out)
@@ -177,7 +177,7 @@ impl sqlx::Type<Sqlite> for Version {
177 177 }
178 178 }
179 179
180 - impl<'q> sqlx::Encode<'q, Sqlite> for Version {
180 + impl sqlx::Encode<'_, Sqlite> for Version {
181 181 fn encode_by_ref(
182 182 &self,
183 183 buf: &mut <Sqlite as sqlx::Database>::ArgumentBuffer,
@@ -267,7 +267,7 @@ impl sqlx::Type<Sqlite> for GitSha {
267 267 }
268 268 }
269 269
270 - impl<'q> sqlx::Encode<'q, Sqlite> for GitSha {
270 + impl sqlx::Encode<'_, Sqlite> for GitSha {
271 271 fn encode_by_ref(
272 272 &self,
273 273 buf: &mut <Sqlite as sqlx::Database>::ArgumentBuffer,
@@ -394,7 +394,7 @@ impl sqlx::Type<Sqlite> for GateKind {
394 394 }
395 395 }
396 396
397 - impl<'q> sqlx::Encode<'q, Sqlite> for GateKind {
397 + impl sqlx::Encode<'_, Sqlite> for GateKind {
398 398 fn encode_by_ref(
399 399 &self,
400 400 buf: &mut <Sqlite as sqlx::Database>::ArgumentBuffer,
@@ -124,7 +124,7 @@ pub fn channel() -> EventTx {
124 124 /// Send an event without caring whether anyone is listening. `GateLogChunk` goes
125 125 /// to the high-rate log channel, everything else to the status channel.
126 126 pub fn emit(bus: &EventTx, event: Event) {
127 - eventbus::emit(bus, event)
127 + eventbus::emit(bus, event);
128 128 }
129 129
130 130 #[cfg(test)]
@@ -248,7 +248,9 @@ mod tests {
248 248 let err = rx.recv().await.expect_err("expected Lagged");
249 249 match err {
250 250 tokio::sync::broadcast::error::RecvError::Lagged(n) => assert!(n >= 10),
251 - other => panic!("unexpected error: {other:?}"),
251 + other @ tokio::sync::broadcast::error::RecvError::Closed => {
252 + panic!("unexpected error: {other:?}")
253 + }
252 254 }
253 255 }
254 256 }
@@ -162,7 +162,7 @@ pub async fn run(ctx: &GateCtx, gate: &Gate) -> Result<GateOutcome> {
162 162 .bind(Utc::now().to_rfc3339())
163 163 .bind(outcome.status_str())
164 164 .bind(&outcome_json)
165 - .bind(outcome.log_ref.as_ref().map(|l| l.as_str()))
165 + .bind(outcome.log_ref.as_ref().map(super::outcome::LogRef::as_str))
166 166 .bind(id)
167 167 .execute(&ctx.pool)
168 168 .await?;
@@ -822,8 +822,7 @@ fn cargo_test_command(
822 822 // to /var/run/postgresql on Debian/Ubuntu when host is unspecified.
823 823 let test_url = scratch_url
824 824 .split_once('?')
825 - .map(|(base, _)| base)
826 - .unwrap_or(scratch_url);
825 + .map_or(scratch_url, |(base, _)| base);
827 826 cmd.env("TEST_DATABASE_URL", test_url);
828 827 }
829 828 cmd
@@ -1081,7 +1080,10 @@ async fn clean_stale_test_dbs(db_url: &str) {
1081 1080 /// psql's exit. pipefail is a bash builtin (not POSIX sh), so the runner uses
1082 1081 /// `bash -c`.
1083 1082 fn restore_shell(db_url: &str, dump: &str) -> String {
1084 - if dump.ends_with(".gz") {
1083 + if std::path::Path::new(dump)
1084 + .extension()
1085 + .is_some_and(|ext| ext.eq_ignore_ascii_case("gz"))
1086 + {
1085 1087 format!(
1086 1088 "set -o pipefail; gunzip -c {q} | psql -v ON_ERROR_STOP=1 {url}",
1087 1089 q = shell_escape(dump),
@@ -1135,8 +1137,7 @@ fn split_pg_password(db_url: &str) -> (String, Option<String>) {
1135 1137 // between the first ':' and the '@' within the userinfo of that authority.
1136 1138 let authority_end = db_url[after..]
1137 1139 .find(['/', '?', '#'])
1138 - .map(|i| after + i)
1139 - .unwrap_or(db_url.len());
1140 + .map_or(db_url.len(), |i| after + i);
1140 1141 let Some(at) = db_url[after..authority_end].find('@').map(|i| after + i) else {
1141 1142 return (db_url.to_string(), None);
1142 1143 };
@@ -1326,7 +1327,7 @@ async fn code_smoke_docs_check(
1326 1327 .stdout(std::process::Stdio::piped())
1327 1328 .stderr(std::process::Stdio::piped())
1328 1329 .kill_on_drop(true);
1329 - let out = match tokio::time::timeout(std::time::Duration::from_secs(60), cmd.output()).await {
1330 + let out = match tokio::time::timeout(std::time::Duration::from_mins(1), cmd.output()).await {
1330 1331 Ok(Ok(out)) => out,
1331 1332 Ok(Err(e)) => {
1332 1333 log_buf.extend_from_slice(format!("docs check spawn failed: {e}\n").as_bytes());
@@ -1822,8 +1823,7 @@ async fn probe_node(probe: &NodeProbe) -> std::result::Result<(), String> {
1822 1823 let url = probe
1823 1824 .health_url
1824 1825 .as_deref()
1825 - .map(sh_quote)
1826 - .unwrap_or_else(|| "''".to_string());
1826 + .map_or_else(|| "''".to_string(), sh_quote);
1827 1827 // One executor round-trip with the retry loop on the node: is-active, then
1828 1828 // (if a url is set) curl it for a 2xx. Exit 0 only when both hold.
1829 1829 let script = format!(
@@ -1849,8 +1849,7 @@ async fn probe_node(probe: &NodeProbe) -> std::result::Result<(), String> {
1849 1849 let code = out
1850 1850 .status
1851 1851 .code()
1852 - .map(|c| c.to_string())
1853 - .unwrap_or_else(|| "signal".to_string());
1852 + .map_or_else(|| "signal".to_string(), |c| c.to_string());
1854 1853 let stderr: String = String::from_utf8_lossy(&out.stderr)
1855 1854 .chars()
1856 1855 .take(200)
@@ -1982,8 +1981,7 @@ async fn manual_confirm(ctx: &GateCtx) -> Result<GateOutcome> {
1982 1981 match prior_at {
1983 1982 Some(at_str) => {
1984 1983 let at = chrono::DateTime::parse_from_rfc3339(&at_str)
1985 - .map(|d| d.with_timezone(&Utc))
1986 - .unwrap_or_else(|_| Utc::now());
1984 + .map_or_else(|_| Utc::now(), |d| d.with_timezone(&Utc));
1987 1985 Ok(GateOutcome::passed(PassNote::OperatorConfirmed { at }))
1988 1986 }
1989 1987 None => Ok(GateOutcome::blocked(
@@ -136,9 +136,8 @@ pub async fn checkout_worktree(bare: &Path, sha: &str, dest: &Path) -> Result<()
136 136 /// sha) is reported as "no match" rather than an error — the caller recreates.
137 137 async fn worktree_head_matches(bare: &Path, dest: &Path, sha: &str) -> Result<bool> {
138 138 let want = resolve_commit(bare, sha).await?;
139 - let have = match worktree_head(dest).await {
140 - Ok(h) => h,
141 - Err(_) => return Ok(false),
139 + let Ok(have) = worktree_head(dest).await else {
140 + return Ok(false);
142 141 };
143 142 Ok(have == want)
144 143 }
@@ -85,7 +85,7 @@ async fn run() -> Result<()> {
85 85 "settled orphaned 'building' run(s) from a prior daemon"
86 86 ),
87 87 Err(e) => {
88 - tracing::error!(error = %e, "failed to reconcile orphaned 'building' runs at startup")
88 + tracing::error!(error = %e, "failed to reconcile orphaned 'building' runs at startup");
89 89 }
90 90 }
91 91 sync::sync(&pool, &topo).await?;
@@ -119,7 +119,7 @@ async fn run() -> Result<()> {
119 119 match &api_token {
120 120 Some(_) => tracing::info!("deploy endpoints require a bearer token"),
121 121 None => {
122 - tracing::warn!(%addr, "SANDO_API_TOKEN unset; deploy endpoints are UNAUTHENTICATED (loopback bind)")
122 + tracing::warn!(%addr, "SANDO_API_TOKEN unset; deploy endpoints are UNAUTHENTICATED (loopback bind)");
123 123 }
124 124 }
125 125
@@ -45,6 +45,7 @@ impl GateOutcome {
45 45 log_ref: None,
46 46 }
47 47 }
48 + #[must_use]
48 49 pub fn with_log_ref(mut self, log_ref: LogRef) -> Self {
49 50 self.log_ref = Some(log_ref);
50 51 self
@@ -73,8 +73,7 @@ async fn require_bearer(
73 73 let peer = req
74 74 .extensions()
75 75 .get::<axum::extract::ConnectInfo<std::net::SocketAddr>>()
76 - .map(|ci| ci.0.to_string())
77 - .unwrap_or_else(|| "unknown".into());
76 + .map_or_else(|| "unknown".into(), |ci| ci.0.to_string());
78 77 tracing::info!(path = %path, peer = %peer, "authenticated deploy mutation");
79 78 }
80 79 next.run(req).await
@@ -3,7 +3,7 @@
3 3 //! partial-state flags, and the promote-time gate-satisfaction check.
4 4 //! routes/mod.rs stays HTTP glue that calls into these.
5 5
6 - use super::*;
6 + use super::{AppState, Json, PromoteBody, Result};
7 7
8 8 pub(super) async fn promote_inner(
9 9 s: AppState,
@@ -474,7 +474,7 @@ pub(super) async fn rollback_deployed_nodes(
474 474 };
475 475 let Some(staged_dir) = std::path::PathBuf::from(&bin)
476 476 .parent()
477 - .map(|p| p.to_path_buf())
477 + .map(std::path::Path::to_path_buf)
478 478 else {
479 479 tracing::error!(tier = %tier, prev = prev_version,
480 480 "canary rollback: previous artifact_path has no parent dir; nodes left on the new version");
@@ -340,8 +340,7 @@ fn elapsed_seconds(started_at: &str, finished_at: Option<&str>) -> i64 {
340 340 };
341 341 let end = match finished_at {
342 342 Some(f) => chrono::DateTime::parse_from_rfc3339(f)
343 - .map(|d| d.with_timezone(&Utc))
344 - .unwrap_or_else(|_| Utc::now()),
343 + .map_or_else(|_| Utc::now(), |d| d.with_timezone(&Utc)),
345 344 None => Utc::now(),
346 345 };
347 346 (end - start.with_timezone(&Utc)).num_seconds().max(0)
@@ -366,11 +365,13 @@ pub async fn first_failed_gate_summary(pool: &SqlitePool, version: &Version) ->
366 365 let outcome_json: Option<String> = row.get("outcome_json");
367 366 let summary = outcome_json
368 367 .and_then(|s| serde_json::from_str::<crate::outcome::GateOutcome>(&s).ok())
369 - .map(|o| match o.status {
370 - crate::outcome::GateStatus::Failed { failure } => failure.summary(),
371 - other => format!("{:?}", other),
372 - })
373 - .unwrap_or_else(|| "gate failed".to_string());
368 + .map_or_else(
369 + || "gate failed".to_string(),
370 + |o| match o.status {
371 + crate::outcome::GateStatus::Failed { failure } => failure.summary(),
372 + other => format!("{other:?}"),
373 + },
374 + );
374 375 Some(format!("{kind}: {summary}"))
375 376 }
376 377
@@ -407,26 +408,26 @@ mod tests {
407 408 let pool = pool().await;
408 409 // Two in-flight runs (as if the daemon died mid-build) + one already
409 410 // settled, which must be left untouched.
410 - let a = create(&pool, "aaaaaaa").await.unwrap();
411 - let b = create(&pool, "bbbbbbb").await.unwrap();
412 - let c = create(&pool, "ccccccc").await.unwrap();
413 - mark_passed(&pool, c).await.unwrap();
414 -
415 - let n = recover_orphaned_running(&pool).await.unwrap();
416 - assert_eq!(n, 2, "both 'building' runs reconciled");
417 -
418 - for id in [a, b] {
419 - let v = get(&pool, id).await.unwrap().unwrap();
420 - assert_eq!(v.result, "aborted");
421 - assert_eq!(v.phase, "done");
422 - assert!(v.finished_at.is_some());
411 + let run_a = create(&pool, "aaaaaaa").await.unwrap();
412 + let run_b = create(&pool, "bbbbbbb").await.unwrap();
413 + let run_c = create(&pool, "ccccccc").await.unwrap();
414 + mark_passed(&pool, run_c).await.unwrap();
415 +
416 + let reconciled = recover_orphaned_running(&pool).await.unwrap();
417 + assert_eq!(reconciled, 2, "both 'building' runs reconciled");
418 +
419 + for id in [run_a, run_b] {
420 + let rec = get(&pool, id).await.unwrap().unwrap();
421 + assert_eq!(rec.result, "aborted");
422 + assert_eq!(rec.phase, "done");
423 + assert!(rec.finished_at.is_some());
423 424 assert_eq!(
424 - v.failure_summary.as_deref(),
425 + rec.failure_summary.as_deref(),
425 426 Some("daemon restarted mid-build")
426 427 );
427 428 }
428 429 // The already-settled run is unchanged.
429 - assert_eq!(get(&pool, c).await.unwrap().unwrap().result, "passed");
430 + assert_eq!(get(&pool, run_c).await.unwrap().unwrap().result, "passed");
430 431
431 432 // Idempotent: a second pass finds nothing to do.
432 433 assert_eq!(recover_orphaned_running(&pool).await.unwrap(), 0);