From cafbddb861a62b4096a6be711cdf33bfdd20b77c Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 16 Jun 2026 16:45:08 +0800 Subject: [PATCH] refactor: replace server-only dead_code allows with cfg(feature = "server") Replace #[allow(dead_code)] on server-only helpers with #[cfg(feature = "server")] to make the server/WASM split explicit and avoid compiling unused server logic into the WASM frontend. - Gate password/session/auth/comment helpers and model parsers with server feature - Gate related imports and tests accordingly - Remove genuinely unused CreatePostRequest and CreateCommentRequest - Keep #[allow(dead_code)] for true dead code (stub methods, unused public APIs) - Use #[cfg(any(target_arch = "wasm32", test))] for THEME_KEY - Update AGENTS.md note --- AGENTS.md | 2 +- src/api/auth.rs | 8 ++++---- src/api/comments/helpers.rs | 11 +++++------ src/api/comments/types.rs | 22 +++------------------- src/api/posts/types.rs | 20 -------------------- src/auth/password.rs | 10 ++++++---- src/auth/session.rs | 9 ++++++--- src/models/comment.rs | 4 +++- src/models/post.rs | 5 +++-- src/models/user.rs | 3 ++- src/theme.rs | 2 +- 11 files changed, 34 insertions(+), 62 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d0ff3bb..d18f18d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,7 +71,7 @@ Dioxus 0.7 fullstack project with **two independent gates** — the most common **Stub pattern**: `src/db/mod.rs` provides a `DummyPool` when `server` feature is disabled — do not remove. -**Dead code allowances**: `src/auth/password.rs` and `src/auth/session.rs` carry `#[allow(dead_code)]` because the compiler sees them as unused in WASM builds where server function bodies are stripped. +**Server-only helpers**: `src/auth/password.rs`, `src/auth/session.rs`, `src/api/auth.rs`, `src/api/comments/helpers.rs`, and several model helper methods are gated with `#[cfg(feature = "server")]` because they are only called from server function bodies, which are stripped in WASM builds. ## Dual API Architecture diff --git a/src/api/auth.rs b/src/api/auth.rs index f16de87..fbf9623 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -23,7 +23,7 @@ use crate::db::pool::get_conn; use crate::models::user::{User, UserRole}; use crate::models::user::PublicUser; -#[allow(dead_code)] +#[cfg(feature = "server")] fn validate_username(username: &str) -> Result<(), String> { if username.len() < 3 || username.len() > 50 { return Err("用户名长度必须在 3-50 字符之间".to_string()); @@ -34,7 +34,7 @@ fn validate_username(username: &str) -> Result<(), String> { Ok(()) } -#[allow(dead_code)] +#[cfg(feature = "server")] fn validate_email(email: &str) -> Result<(), String> { let re = regex::Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap(); if !re.is_match(email) { @@ -43,7 +43,7 @@ fn validate_email(email: &str) -> Result<(), String> { Ok(()) } -#[allow(dead_code)] +#[cfg(feature = "server")] fn validate_password(password: &str) -> Result<(), String> { if password.len() < 8 { return Err("密码长度至少 8 位".to_string()); @@ -387,7 +387,7 @@ pub async fn get_current_admin_user() -> Result { Ok(user) } -#[cfg(test)] +#[cfg(all(test, feature = "server"))] mod tests { use super::*; diff --git a/src/api/comments/helpers.rs b/src/api/comments/helpers.rs index c12f7f8..5bfec89 100644 --- a/src/api/comments/helpers.rs +++ b/src/api/comments/helpers.rs @@ -1,7 +1,6 @@ //! 评论模块的辅助函数:数据转换、校验、哈希与头像生成。 //! -//! 大部分工具函数仅在 `feature = "server"` 启用的服务端构建中使用; -//! 校验函数同时在前端构建中保留签名,避免编译器提示未使用。 +//! 所有工具函数仅在 `feature = "server"` 启用的服务端构建中使用。 #![allow(clippy::unused_unit, deprecated)] @@ -87,7 +86,7 @@ pub fn format_relative_time(dt: chrono::DateTime) -> String { } /// 校验评论作者昵称:非空且不超过 50 字符。 -#[allow(dead_code)] +#[cfg(feature = "server")] pub fn validate_comment_name(name: &str) -> Result<(), String> { let trimmed = name.trim(); if trimmed.is_empty() { @@ -100,7 +99,7 @@ pub fn validate_comment_name(name: &str) -> Result<(), String> { } /// 校验评论作者邮箱格式。 -#[allow(dead_code)] +#[cfg(feature = "server")] pub fn validate_comment_email(email: &str) -> Result<(), String> { let re = regex::Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap(); if !re.is_match(email.trim()) { @@ -110,7 +109,7 @@ pub fn validate_comment_email(email: &str) -> Result<(), String> { } /// 校验评论作者网址:为空时允许,非空时必须以 http:// 或 https:// 开头且不超过 200 字符。 -#[allow(dead_code)] +#[cfg(feature = "server")] pub fn validate_comment_url(url: &str) -> Result<(), String> { let trimmed = url.trim(); if trimmed.is_empty() { @@ -127,7 +126,7 @@ pub fn validate_comment_url(url: &str) -> Result<(), String> { } /// 校验评论内容:非空且不超过 10000 字符。 -#[allow(dead_code)] +#[cfg(feature = "server")] pub fn validate_comment_content(content: &str) -> Result<(), String> { let trimmed = content.trim(); if trimmed.is_empty() { diff --git a/src/api/comments/types.rs b/src/api/comments/types.rs index 0de9c23..9a0d208 100644 --- a/src/api/comments/types.rs +++ b/src/api/comments/types.rs @@ -3,24 +3,6 @@ use crate::models::comment::{AdminComment, PublicComment}; use serde::{Deserialize, Serialize}; -/// 创建评论请求体(客户端使用)。 -#[derive(Debug, Clone, Serialize, Deserialize)] -#[allow(dead_code)] -pub struct CreateCommentRequest { - /// 目标文章 id。 - pub post_id: i32, - /// 父评论 id,顶层评论为 None。 - pub parent_id: Option, - /// 评论者昵称。 - pub author_name: String, - /// 评论者邮箱。 - pub author_email: String, - /// 评论者个人网址。 - pub author_url: Option, - /// 评论 Markdown 原文。 - pub content_md: String, -} - /// 创建/审核/删除评论的统一响应结构。 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommentResponse { @@ -52,13 +34,15 @@ pub struct CommentTreeResponse { /// 评论计数响应。 #[derive(Debug, Clone, Serialize, Deserialize)] -#[allow(dead_code)] pub struct CommentCountResponse { /// 评论数量。 pub count: i64, } /// 待审核评论列表响应。 +/// +/// 当前前端未直接调用 `get_pending_comments`,此类型仅在服务端函数体中构造; +/// 保留 `#[allow(dead_code)]` 以避免 WASM 构建中因函数体被剥离而产生的未使用警告。 #[derive(Debug, Clone, Serialize, Deserialize)] #[allow(dead_code)] pub struct PendingCommentsResponse { diff --git a/src/api/posts/types.rs b/src/api/posts/types.rs index 24f5092..4f1e118 100644 --- a/src/api/posts/types.rs +++ b/src/api/posts/types.rs @@ -2,26 +2,6 @@ use crate::models::post::{Post, PostStats, Tag}; -/// 创建/更新文章请求体(客户端使用)。 -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[allow(dead_code)] -pub struct CreatePostRequest { - /// 文章标题。 - pub title: String, - /// 自定义 slug,为空时由标题自动生成。 - pub slug: Option, - /// 文章摘要,为空时自动从正文提取。 - pub summary: Option, - /// Markdown 格式正文。 - pub content_md: String, - /// 文章状态(如 draft / published)。 - pub status: String, - /// 标签列表。 - pub tags: Vec, - /// 封面图 URL。 - pub cover_image: Option, -} - /// 创建/更新/删除文章的统一响应结构。 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct CreatePostResponse { diff --git a/src/auth/password.rs b/src/auth/password.rs index c4348ff..d7284ed 100644 --- a/src/auth/password.rs +++ b/src/auth/password.rs @@ -1,15 +1,17 @@ //! 密码哈希与校验(Argon2)。 //! //! 使用随机 salt 生成 PHC 字符串格式哈希,并通过 Argon2 验证。 -//! `#[allow(dead_code)]` 用于避免 WASM 构建中服务端函数体被剥离后的未使用警告。 +//! 仅在 `feature = "server"` 启用的服务端构建中使用。 +#[cfg(feature = "server")] use argon2::{ password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Argon2, }; +#[cfg(feature = "server")] use rand::rngs::OsRng; -#[allow(dead_code)] +#[cfg(feature = "server")] /// 使用 Argon2 对明文密码进行哈希。 pub fn hash_password(password: &str) -> Result { let salt = SaltString::generate(&mut OsRng); @@ -18,7 +20,7 @@ pub fn hash_password(password: &str) -> Result Result { let parsed_hash = PasswordHash::new(hash)?; @@ -30,7 +32,7 @@ pub fn verify_password(password: &str, hash: &str) -> Result String { Uuid::new_v4().to_string() } -#[allow(dead_code)] +#[cfg(feature = "server")] /// 使用 SHA-256 对 token 进行哈希,用于数据库存储。 pub fn hash_token(token: &str) -> String { let mut hasher = Sha256::new(); @@ -22,7 +25,7 @@ pub fn hash_token(token: &str) -> String { hex::encode(hasher.finalize()) } -#[allow(dead_code)] +#[cfg(feature = "server")] /// 返回默认会话过期时间(当前时间 + 30 天)。 pub fn default_expiry() -> DateTime { Utc::now() + Duration::days(30) diff --git a/src/models/comment.rs b/src/models/comment.rs index 355f6d7..248b455 100644 --- a/src/models/comment.rs +++ b/src/models/comment.rs @@ -33,7 +33,7 @@ impl CommentStatus { } /// 将 CommentStatus 序列化为小写字符串。 - #[allow(dead_code)] + #[cfg(test)] pub fn as_str(&self) -> &'static str { match self { Self::Pending => "pending", @@ -145,6 +145,7 @@ mod tests { use super::*; #[test] + #[cfg(feature = "server")] fn comment_status_from_str() { assert_eq!(CommentStatus::from_str("pending"), CommentStatus::Pending); assert_eq!(CommentStatus::from_str("approved"), CommentStatus::Approved); @@ -153,6 +154,7 @@ mod tests { } #[test] + #[cfg(feature = "server")] fn comment_status_from_str_unknown_defaults_to_pending() { assert_eq!(CommentStatus::from_str("unknown"), CommentStatus::Pending); assert_eq!(CommentStatus::from_str(""), CommentStatus::Pending); diff --git a/src/models/post.rs b/src/models/post.rs index b72933b..265da79 100644 --- a/src/models/post.rs +++ b/src/models/post.rs @@ -17,7 +17,6 @@ pub enum PostStatus { impl PostStatus { /// 将状态序列化为数据库或 API 使用的小写字符串。 - #[allow(dead_code)] pub fn as_str(&self) -> &'static str { match self { PostStatus::Draft => "draft", @@ -26,7 +25,7 @@ impl PostStatus { } /// 将字符串解析为 PostStatus,无法识别时返回 None。 - #[allow(dead_code)] + #[cfg(feature = "server")] pub fn from_str(s: &str) -> Option { match s { "draft" => Some(PostStatus::Draft), @@ -175,6 +174,7 @@ mod tests { } #[test] + #[cfg(feature = "server")] fn post_status_from_str() { assert_eq!(PostStatus::from_str("draft"), Some(PostStatus::Draft)); assert_eq!( @@ -192,6 +192,7 @@ mod tests { } #[test] + #[cfg(feature = "server")] fn post_status_roundtrip() { for status in [PostStatus::Draft, PostStatus::Published] { assert_eq!(PostStatus::from_str(status.as_str()), Some(status.clone())); diff --git a/src/models/user.rs b/src/models/user.rs index 29fdf27..f588061 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -17,7 +17,7 @@ pub enum UserRole { impl UserRole { /// 将数据库中的角色字符串解析为 UserRole,无法识别时返回 None。 - #[allow(dead_code)] + #[cfg(feature = "server")] pub fn from_str(s: &str) -> Option { match s { "admin" => Some(UserRole::Admin), @@ -89,6 +89,7 @@ mod tests { } #[test] + #[cfg(feature = "server")] fn user_role_from_str() { assert_eq!(UserRole::from_str("admin"), Some(UserRole::Admin)); assert_eq!(UserRole::from_str("blocked"), Some(UserRole::Blocked)); diff --git a/src/theme.rs b/src/theme.rs index 54d2e9a..8bc55e5 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; /// localStorage 中存储主题值的键名。 -#[allow(dead_code)] +#[cfg(any(target_arch = "wasm32", test))] const THEME_KEY: &str = "yggdrasil-theme"; /// 应用主题枚举。