- 新增二维码生成服务层,支持自定义内容和配置 - 移除 Platform 抽象层,简化为三层架构 - 更新开发文档和架构说明 - 添加前端二维码生成页面和状态管理
1019 lines
23 KiB
Markdown
1019 lines
23 KiB
Markdown
# Tauri 应用开发指南
|
||
|
||
## 目录
|
||
|
||
- [架构概述](#架构概述)
|
||
- [添加新功能的完整流程](#添加新功能的完整流程)
|
||
- [各层开发规范](#各层开发规范)
|
||
- [代码示例](#代码示例)
|
||
- [最佳实践](#最佳实践)
|
||
- [测试规范](#测试规范)
|
||
|
||
---
|
||
|
||
## 架构概述
|
||
|
||
### 三层架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Command 层 │
|
||
│ 职责:参数验证、调用 Service、结果转换 │
|
||
│ 文件:src/commands/*.rs │
|
||
├─────────────────────────────────────────────────┤
|
||
│ Service 层 │
|
||
│ 职责:业务逻辑、流程编排、状态管理 │
|
||
│ 文件:src/services/*.rs │
|
||
├─────────────────────────────────────────────────┤
|
||
│ Utils 层 │
|
||
│ 职责:纯算法、无状态工具函数 │
|
||
│ 文件:src/utils/*.rs │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 依赖原则
|
||
|
||
- **单向依赖**:上层依赖下层,下层不依赖上层
|
||
- **依赖链**:Command → Service → Utils
|
||
- **Utils 完全独立**:只依赖标准库和 models
|
||
- **Service 可以直接调用 Windows API**
|
||
|
||
---
|
||
|
||
## 添加新功能的完整流程
|
||
|
||
### 步骤 1:需求分析
|
||
|
||
在开始编码前,明确以下内容:
|
||
|
||
1. **功能描述**:这个功能做什么?
|
||
2. **用户交互**:用户如何触发这个功能?
|
||
3. **输入输出**:需要什么参数?返回什么结果?
|
||
4. **错误场景**:哪些情况下会出错?如何处理?
|
||
|
||
### 步骤 2:设计数据模型(如需要)
|
||
|
||
如果新功能需要新的数据结构,在 `models/` 中定义:
|
||
|
||
```rust
|
||
// src/models/new_feature.rs
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
/// 新功能的配置信息
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct NewFeatureConfig {
|
||
/// 配置项 1
|
||
pub option1: String,
|
||
/// 配置项 2
|
||
pub option2: u32,
|
||
}
|
||
|
||
/// 新功能的输出结果
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct NewFeatureResult {
|
||
/// 状态码
|
||
pub status: u16,
|
||
/// 结果数据
|
||
pub data: String,
|
||
}
|
||
```
|
||
|
||
**规范**:
|
||
- ✅ 使用中文注释
|
||
- ✅ 每个字段都要有注释说明含义
|
||
- ✅ 实现 `Debug`, `Clone`, `Serialize`, `Deserialize`
|
||
- ✅ 结构体命名使用大驼峰(PascalCase)
|
||
- ✅ 字段命名使用下划线命名(snake_case)
|
||
|
||
### 步骤 3:实现工具函数(如果需要纯算法)
|
||
|
||
如果有纯算法逻辑,在 `utils/` 中实现:
|
||
|
||
```rust
|
||
// src/utils/new_algorithm.rs
|
||
|
||
/// 新算法函数
|
||
///
|
||
/// 对输入数据进行处理
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `input` - 输入数据
|
||
///
|
||
/// # 返回
|
||
///
|
||
/// 返回处理后的结果
|
||
///
|
||
/// # 示例
|
||
///
|
||
/// ```
|
||
/// use crate::utils::new_algorithm::process_data;
|
||
///
|
||
/// let result = process_data(42);
|
||
/// assert_eq!(result, 84);
|
||
/// ```
|
||
pub fn process_data(input: u32) -> u32 {
|
||
input * 2
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_process_data() {
|
||
assert_eq!(process_data(2), 4);
|
||
assert_eq!(process_data(0), 0);
|
||
}
|
||
}
|
||
```
|
||
|
||
**规范**:
|
||
- ✅ 纯函数,无副作用
|
||
- ✅ 添加详细的文档注释
|
||
- ✅ 包含单元测试
|
||
- ✅ 不依赖任何外部状态
|
||
|
||
### 步骤 4:实现业务逻辑(Service 层)
|
||
|
||
```rust
|
||
// src/services/new_feature_service.rs
|
||
|
||
//! 新功能服务
|
||
//!
|
||
//! 提供新功能的核心业务逻辑
|
||
|
||
use crate::error::AppResult;
|
||
use crate::models::new_feature::{NewFeatureConfig, NewFeatureResult};
|
||
|
||
/// 新功能服务
|
||
pub struct NewFeatureService;
|
||
|
||
impl NewFeatureService {
|
||
/// 执行新功能
|
||
///
|
||
/// 根据配置执行相应的操作
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `config` - 功能配置
|
||
///
|
||
/// # 返回
|
||
///
|
||
/// 返回执行结果
|
||
///
|
||
/// # 错误
|
||
///
|
||
/// - 配置无效时返回 `AppError::InvalidColorData`
|
||
pub fn execute(&self, config: &NewFeatureConfig) -> AppResult<NewFeatureResult> {
|
||
// 1. 参数验证
|
||
if config.option1.is_empty() {
|
||
return Err(AppError::InvalidColorData(
|
||
"配置项 option1 不能为空".to_string()
|
||
));
|
||
}
|
||
|
||
// 2. 业务逻辑
|
||
let result = format!("处理: {} - {}", config.option1, config.option2);
|
||
|
||
// 3. 返回结果
|
||
Ok(NewFeatureResult {
|
||
status: 200,
|
||
data: result,
|
||
})
|
||
}
|
||
|
||
/// 执行新功能(异步版本)
|
||
///
|
||
/// 异步执行新功能
|
||
pub async fn execute_async(&self, config: NewFeatureConfig) -> AppResult<NewFeatureResult> {
|
||
// 使用 tokio 或其他异步运行时
|
||
Ok(self.execute(&config)?)
|
||
}
|
||
}
|
||
```
|
||
|
||
**规范**:
|
||
- ✅ 使用 struct 命名空间(如 `NewFeatureService`)
|
||
- ✅ 所有方法返回 `AppResult<T>`
|
||
- ✅ 参数验证放在 Service 层
|
||
- ✅ 可以直接调用 Windows API
|
||
- ✅ 添加详细的文档注释
|
||
- ✅ 包含同步和异步两个版本(如需要)
|
||
|
||
### 步骤 5:创建 Tauri 命令(Command 层)
|
||
|
||
```rust
|
||
// src/commands/new_feature_commands.rs
|
||
|
||
//! 新功能命令
|
||
//!
|
||
//! 定义新功能相关的 Tauri 命令
|
||
|
||
use crate::models::new_feature::{NewFeatureConfig, NewFeatureResult};
|
||
use crate::services::new_feature_service::NewFeatureService;
|
||
|
||
/// 执行新功能命令
|
||
///
|
||
/// Tauri 命令,用于从前端调用新功能
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `config` - 功能配置
|
||
///
|
||
/// # 返回
|
||
///
|
||
/// 返回执行结果,包含状态码和数据
|
||
///
|
||
/// # 前端调用示例
|
||
///
|
||
/// ```typescript
|
||
/// import { invoke } from '@tauri-apps/api/tauri';
|
||
///
|
||
/// const result = await invoke('execute_new_feature', {
|
||
/// config: {
|
||
/// option1: 'test',
|
||
/// option2: 100
|
||
/// }
|
||
/// });
|
||
/// console.log(result.status); // 200
|
||
/// console.log(result.data); // "处理: test - 100"
|
||
/// ```
|
||
#[tauri::command]
|
||
pub fn execute_new_feature(config: NewFeatureConfig) -> Result<NewFeatureResult, String> {
|
||
// 创建服务实例
|
||
let service = NewFeatureService;
|
||
|
||
// 调用服务层
|
||
service
|
||
.execute(&config)
|
||
.map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 异步执行新功能命令
|
||
///
|
||
/// 异步版本的执行命令
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `config` - 功能配置
|
||
///
|
||
/// # 返回
|
||
///
|
||
/// 返回执行结果
|
||
#[tauri::command]
|
||
pub async fn execute_new_feature_async(config: NewFeatureConfig) -> Result<NewFeatureResult, String> {
|
||
let service = NewFeatureService;
|
||
service
|
||
.execute_async(config)
|
||
.await
|
||
.map_err(|e| e.to_string())
|
||
}
|
||
```
|
||
|
||
**规范**:
|
||
- ✅ 使用 `#[tauri::command]` 宏
|
||
- ✅ 返回类型为 `Result<T, String>`
|
||
- ✅ 使用 `.map_err(|e| e.to_string())` 转换错误
|
||
- ✅ 包含前端调用示例
|
||
- ✅ 参数使用结构体,便于扩展
|
||
- ✅ 添加详细的文档注释
|
||
|
||
### 步骤 6:注册模块
|
||
|
||
更新各模块的 `mod.rs` 和 `lib.rs`:
|
||
|
||
```rust
|
||
// src/models/mod.rs
|
||
pub mod color;
|
||
pub mod new_feature; // 新增
|
||
|
||
pub use color::{ColorInfo, RgbInfo, HslInfo};
|
||
pub use new_feature::{NewFeatureConfig, NewFeatureResult}; // 新增
|
||
|
||
// src/commands/mod.rs
|
||
pub mod color_commands;
|
||
pub mod window_commands;
|
||
pub mod new_feature_commands; // 新增
|
||
|
||
// src/lib.rs
|
||
.invoke_handler(tauri::generate_handler![
|
||
// ... 其他命令
|
||
commands::new_feature_commands::execute_new_feature,
|
||
commands::new_feature_commands::execute_new_feature_async,
|
||
])
|
||
```
|
||
|
||
**规范**:
|
||
- ✅ 按字母顺序排列
|
||
- ✅ 导出常用类型
|
||
- ✅ 注册所有新命令
|
||
|
||
### 步骤 7:错误处理扩展(如需要)
|
||
|
||
如果需要新的错误类型,在 `error.rs` 中添加:
|
||
|
||
```rust
|
||
// src/error.rs
|
||
|
||
#[derive(Debug)]
|
||
pub enum AppError {
|
||
// ... 现有错误类型
|
||
|
||
/// 新功能相关错误
|
||
NewFeatureFailed(String),
|
||
}
|
||
|
||
impl fmt::Display for AppError {
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
match self {
|
||
// ... 现有错误
|
||
AppError::NewFeatureFailed(msg) => write!(f, "新功能执行失败: {}", msg),
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**规范**:
|
||
- ✅ 错误类型使用中文描述
|
||
- ✅ 携带详细的错误信息(`String`)
|
||
- ✅ 在 `Display` 实现中添加上下文
|
||
|
||
### 步骤 8:更新前端类型定义
|
||
|
||
在 `src/types/` 或相关位置添加 TypeScript 类型定义:
|
||
|
||
```typescript
|
||
// src/types/new-feature.ts
|
||
|
||
/**
|
||
* 新功能配置
|
||
*/
|
||
export interface NewFeatureConfig {
|
||
/** 配置项 1 */
|
||
option1: string;
|
||
/** 配置项 2 */
|
||
option2: number;
|
||
}
|
||
|
||
/**
|
||
* 新功能结果
|
||
*/
|
||
export interface NewFeatureResult {
|
||
/** 状态码 */
|
||
status: number;
|
||
/** 结果数据 */
|
||
data: string;
|
||
}
|
||
|
||
// 导出 Tauri 命令类型
|
||
export type NewFeatureCommands = {
|
||
execute_new_feature: (config: NewFeatureConfig) => Promise<NewFeatureResult>;
|
||
execute_new_feature_async: (config: NewFeatureConfig) => Promise<NewFeatureResult>;
|
||
};
|
||
```
|
||
|
||
### 步骤 9:编写测试
|
||
|
||
```rust
|
||
// src/services/new_feature_service.rs 中的测试
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_execute_with_valid_config() {
|
||
let service = NewFeatureService;
|
||
let config = NewFeatureConfig {
|
||
option1: "test".to_string(),
|
||
option2: 100,
|
||
};
|
||
|
||
let result = service.execute(&config).unwrap();
|
||
assert_eq!(result.status, 200);
|
||
}
|
||
|
||
#[test]
|
||
fn test_execute_with_empty_option1() {
|
||
let service = NewFeatureService;
|
||
let config = NewFeatureConfig {
|
||
option1: "".to_string(),
|
||
option2: 100,
|
||
};
|
||
|
||
assert!(service.execute(&config).is_err());
|
||
}
|
||
}
|
||
```
|
||
|
||
**规范**:
|
||
- ✅ 测试函数命名使用 `test_` 前缀
|
||
- ✅ 测试正常场景和错误场景
|
||
- ✅ 使用 `assert!` 和 `assert_eq!` 断言
|
||
- ✅ 运行 `cargo test` 验证
|
||
|
||
### 步骤 10:验证和测试
|
||
|
||
```bash
|
||
# 1. 检查代码编译
|
||
cargo check
|
||
|
||
# 2. 运行测试
|
||
cargo test
|
||
|
||
# 3. 构建应用
|
||
pnpm build
|
||
|
||
# 4. 运行应用
|
||
pnpm tauri dev
|
||
|
||
# 5. 测试功能
|
||
# - 在前端界面测试新功能
|
||
# - 检查控制台输出
|
||
# - 验证错误处理
|
||
```
|
||
|
||
---
|
||
|
||
## 各层开发规范
|
||
|
||
### Models 层规范
|
||
|
||
**职责**:定义数据结构,不包含业务逻辑
|
||
|
||
**规范清单**:
|
||
- ✅ 使用 `serde` 的 `Serialize` 和 `Deserialize`
|
||
- ✅ 实现 `Debug` 和 `Clone`
|
||
- ✅ 所有字段必须有文档注释
|
||
- ✅ 使用 `new()` 方法构造复杂对象
|
||
- ✅ 字段类型优先使用基本类型
|
||
- ✅ 集中定义在一个模块中
|
||
|
||
**示例**:
|
||
```rust
|
||
/// 用户信息
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct UserInfo {
|
||
/// 用户 ID
|
||
pub id: u32,
|
||
/// 用户名
|
||
pub name: String,
|
||
}
|
||
|
||
impl UserInfo {
|
||
/// 创建新用户
|
||
pub fn new(id: u32, name: String) -> Self {
|
||
Self { id, name }
|
||
}
|
||
}
|
||
```
|
||
|
||
### Utils 层规范
|
||
|
||
**职责**:提供纯函数算法,无副作用
|
||
|
||
**规范清单**:
|
||
- ✅ 纯函数,不依赖外部状态
|
||
- ✅ 不使用 `unsafe` 代码
|
||
- ✅ 不执行 I/O 操作
|
||
- ✅ 包含单元测试
|
||
- ✅ 使用清晰的函数命名
|
||
|
||
**示例**:
|
||
```rust
|
||
/// 计算两点之间的距离
|
||
pub fn calculate_distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
|
||
let dx = x2 - x1;
|
||
let dy = y2 - y1;
|
||
(dx * dx + dy * dy).sqrt()
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_calculate_distance() {
|
||
let dist = calculate_distance(0.0, 0.0, 3.0, 4.0);
|
||
assert_eq!(dist, 5.0);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Service 层规范
|
||
|
||
**职责**:业务逻辑实现和流程编排
|
||
|
||
**规范清单**:
|
||
- ✅ 使用 struct 命名空间
|
||
- ✅ 所有方法返回 `AppResult<T>`
|
||
- ✅ 参数验证在 Service 层进行
|
||
- ✅ 可以直接调用 Windows API
|
||
- ✅ 可以调用 Utils 层函数
|
||
- ✅ 提供同步和异步版本(如需要)
|
||
- ✅ 使用详细的中文档注释
|
||
|
||
**示例**:
|
||
```rust
|
||
pub struct FileService;
|
||
|
||
impl FileService {
|
||
pub fn read_and_process(&self, path: &str) -> AppResult<String> {
|
||
// 1. 参数验证
|
||
if path.is_empty() {
|
||
return Err(AppError::InvalidData("路径不能为空".to_string()));
|
||
}
|
||
|
||
// 2. 调用 Windows API 或第三方库
|
||
let content = std::fs::read_to_string(path)?;
|
||
|
||
// 3. 调用 Utils 层处理
|
||
let processed = utils::text::trim_whitespace(&content);
|
||
|
||
Ok(processed.to_string())
|
||
}
|
||
}
|
||
```
|
||
|
||
### Command 层规范
|
||
|
||
**职责**:Tauri 命令处理,连接前端和 Service
|
||
|
||
**规范清单**:
|
||
- ✅ 使用 `#[tauri::command]` 宏
|
||
- ✅ 返回 `Result<T, String>`
|
||
- ✅ 参数使用结构体(便于扩展)
|
||
- ✅ 简洁的适配器代码
|
||
- ✅ 包含前端调用示例
|
||
- ✅ 错误转换为字符串
|
||
|
||
**示例**:
|
||
```rust
|
||
#[tauri::command]
|
||
pub fn process_file(path: String) -> Result<String, String> {
|
||
let service = FileService;
|
||
service
|
||
.read_and_process(&path)
|
||
.map_err(|e| e.to_string())
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 代码示例
|
||
|
||
### 完整示例:添加一个屏幕截图功能
|
||
|
||
#### 1. 定义数据模型
|
||
|
||
```rust
|
||
// src/models/screenshot.rs
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
/// 截图配置
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ScreenshotConfig {
|
||
/// 是否包含光标
|
||
pub include_cursor: bool,
|
||
/// JPEG 质量 (1-100)
|
||
pub quality: u8,
|
||
}
|
||
|
||
/// 截图结果
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ScreenshotResult {
|
||
/// 图片数据(Base64)
|
||
pub data: String,
|
||
/// 图片格式
|
||
pub format: String,
|
||
/// 图片宽度
|
||
pub width: u32,
|
||
/// 图片高度
|
||
pub height: u32,
|
||
}
|
||
```
|
||
|
||
#### 2. 实现 Service
|
||
|
||
```rust
|
||
// src/services/screenshot_service.rs
|
||
|
||
//! 屏幕截图服务
|
||
//!
|
||
//! 提供屏幕截图功能
|
||
|
||
use crate::error::{AppError, AppResult};
|
||
use crate::models::screenshot::{ScreenshotConfig, ScreenshotResult};
|
||
|
||
/// 截图服务
|
||
pub struct ScreenshotService;
|
||
|
||
impl ScreenshotService {
|
||
/// 截取屏幕
|
||
///
|
||
/// 根据配置截取屏幕画面
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `config` - 截图配置
|
||
///
|
||
/// # 返回
|
||
///
|
||
/// 返回截图结果
|
||
pub fn capture(config: &ScreenshotConfig) -> AppResult<ScreenshotResult> {
|
||
// 验证配置
|
||
if config.quality < 1 || config.quality > 100 {
|
||
return Err(AppError::InvalidColorData(
|
||
"图片质量必须在 1-100 之间".to_string()
|
||
));
|
||
}
|
||
|
||
// 调用 Windows API 或使用第三方库
|
||
// 例如:使用 screenshots-rs crate
|
||
let screenshots = screenshots::Screen::all().map_err(|e| {
|
||
AppError::InvalidData(format!("获取屏幕失败: {}", e))
|
||
})?;
|
||
|
||
if let Some(screen) = screenshots.first() {
|
||
let image = screen.capture().map_err(|e| {
|
||
AppError::InvalidData(format!("截图失败: {}", e))
|
||
})?;
|
||
|
||
// 转换为 Base64
|
||
let buffer = image.buffer();
|
||
let base64_data = base64::encode(buffer);
|
||
|
||
Ok(ScreenshotResult {
|
||
data: base64_data,
|
||
format: "png".to_string(),
|
||
width: image.width(),
|
||
height: image.height(),
|
||
})
|
||
} else {
|
||
Err(AppError::InvalidData("未找到屏幕".to_string()))
|
||
}
|
||
}
|
||
|
||
/// 截取指定区域
|
||
pub fn capture_region(
|
||
x: u32,
|
||
y: u32,
|
||
width: u32,
|
||
height: u32,
|
||
config: &ScreenshotConfig,
|
||
) -> AppResult<ScreenshotResult> {
|
||
if width == 0 || height == 0 {
|
||
return Err(AppError::InvalidColorData(
|
||
"宽度和高度必须大于 0".to_string()
|
||
));
|
||
}
|
||
|
||
// 调用 Windows API 实现区域截图
|
||
// ...
|
||
Ok(ScreenshotResult {
|
||
data: "base64_data".to_string(),
|
||
format: "png".to_string(),
|
||
width,
|
||
height,
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. 创建命令
|
||
|
||
```rust
|
||
// src/commands/screenshot_commands.rs
|
||
|
||
//! 截图命令
|
||
|
||
use crate::models::screenshot::{ScreenshotConfig, ScreenshotResult};
|
||
use crate::services::screenshot_service::ScreenshotService;
|
||
|
||
/// 截取屏幕命令
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `config` - 截图配置
|
||
///
|
||
/// # 返回
|
||
///
|
||
/// 返回截图结果
|
||
///
|
||
/// # 前端调用示例
|
||
///
|
||
/// ```typescript
|
||
/// import { invoke } from '@tauri-apps/api/tauri';
|
||
///
|
||
/// const result = await invoke('capture_screen', {
|
||
/// config: {
|
||
/// include_cursor: false,
|
||
/// quality: 90
|
||
/// }
|
||
/// });
|
||
/// console.log(result.data); // Base64 图片数据
|
||
/// ```
|
||
#[tauri::command]
|
||
pub fn capture_screen(config: ScreenshotConfig) -> Result<ScreenshotResult, String> {
|
||
ScreenshotService::capture(&config).map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 截取区域命令
|
||
#[tauri::command]
|
||
pub fn capture_region(
|
||
x: u32,
|
||
y: u32,
|
||
width: u32,
|
||
height: u32,
|
||
config: ScreenshotConfig,
|
||
) -> Result<ScreenshotResult, String> {
|
||
ScreenshotService::capture_region(x, y, width, height, config)
|
||
.map_err(|e| e.to_string())
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 最佳实践
|
||
|
||
### 1. 命名规范
|
||
|
||
| 类型 | 命名风格 | 示例 |
|
||
|------|----------|------|
|
||
| 模块 | snake_case | `color_service.rs` |
|
||
| 结构体 | PascalCase | `ColorInfo`, `NewFeatureService` |
|
||
| 函数 | snake_case | `get_pixel_color`, `toggle_window` |
|
||
| 常量 | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` |
|
||
| Trait | PascalCase + 能力 | `ScreenAccessor`, `CursorController` |
|
||
| 类型别名 | PascalCase + Type | `AppResult`, `JsonResult` |
|
||
|
||
### 2. 文档注释规范
|
||
|
||
```rust
|
||
/// 简短描述(一句话)
|
||
///
|
||
/// 详细描述(可以多行,说明功能、用途、注意事项)
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `param1` - 参数说明
|
||
/// * `param2` - 参数说明
|
||
///
|
||
/// # 返回
|
||
///
|
||
/// 返回值说明
|
||
///
|
||
/// # 错误
|
||
///
|
||
/// - 错误类型1:错误场景
|
||
/// - 错误类型2:错误场景
|
||
///
|
||
/// # 示例
|
||
///
|
||
/// ```
|
||
/// let result = function_name(args);
|
||
/// assert!(result.is_ok());
|
||
/// ```
|
||
pub fn function_name(param1: Type1, param2: Type2) -> AppResult<ReturnType> {
|
||
// 实现
|
||
}
|
||
```
|
||
|
||
### 3. 错误处理规范
|
||
|
||
```rust
|
||
// ✅ 推荐:使用具体的错误类型
|
||
fn do_something() -> AppResult<()> {
|
||
if condition {
|
||
return Err(AppError::InvalidData("数据无效".to_string()));
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
// ❌ 不推荐:直接返回 String
|
||
fn do_something() -> Result<(), String> {
|
||
if condition {
|
||
return Err("错误".to_string());
|
||
}
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
### 4. 参数验证规范
|
||
|
||
```rust
|
||
// ✅ 在 Service 层验证参数
|
||
impl SomeService {
|
||
pub fn execute(&self, input: &str) -> AppResult<Output> {
|
||
// 参数验证
|
||
if input.is_empty() {
|
||
return Err(AppError::InvalidData("输入不能为空".to_string()));
|
||
}
|
||
|
||
if input.len() > 1000 {
|
||
return Err(AppError::InvalidData("输入过长".to_string()));
|
||
}
|
||
|
||
// 业务逻辑
|
||
// ...
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5. 异步处理规范
|
||
|
||
```rust
|
||
// ✅ 提供 sync 和 async 两个版本
|
||
impl SomeService {
|
||
// 同步版本
|
||
pub fn execute(&self, input: &str) -> AppResult<Output> {
|
||
// 直接调用
|
||
}
|
||
|
||
// 异步版本
|
||
pub async fn execute_async(&self, input: String) -> AppResult<Output> {
|
||
// 使用 async/await
|
||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||
self.execute(&input)
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6. 代码组织规范
|
||
|
||
```rust
|
||
// ✅ 推荐:按功能分组
|
||
pub struct ColorService;
|
||
|
||
impl ColorService {
|
||
// 公共方法
|
||
pub fn new() -> Self { Self }
|
||
pub fn do_work(&self) -> AppResult<()> { }
|
||
|
||
// 私有辅助方法
|
||
fn validate(&self) -> AppResult<()> { }
|
||
fn process(&self) -> AppResult<()> { }
|
||
}
|
||
|
||
// ✅ 使用 mod 组织相关功能
|
||
mod color_conversion {
|
||
pub fn rgb_to_hsl() { }
|
||
pub fn hsl_to_rgb() { }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 测试规范
|
||
|
||
### 单元测试
|
||
|
||
```rust
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_normal_case() {
|
||
// 测试正常场景
|
||
let result = function_under_test(normal_input);
|
||
assert_eq!(result, expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_edge_case() {
|
||
// 测试边界条件
|
||
let result = function_under_test(edge_input);
|
||
assert!(result.is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_error_case() {
|
||
// 测试错误场景
|
||
let result = function_under_test(invalid_input);
|
||
assert!(result.is_err());
|
||
}
|
||
}
|
||
```
|
||
|
||
### 集成测试
|
||
|
||
```rust
|
||
// tests/integration_test.rs
|
||
|
||
#[test]
|
||
fn test_full_workflow() {
|
||
// 测试完整的业务流程
|
||
let service = MyService::new();
|
||
let config = MyConfig::default();
|
||
|
||
let result = service.execute(&config).unwrap();
|
||
assert_eq!(result.status, 200);
|
||
}
|
||
```
|
||
|
||
### 运行测试
|
||
|
||
```bash
|
||
# 运行所有测试
|
||
cargo test
|
||
|
||
# 运行特定测试
|
||
cargo test test_color_conversion
|
||
|
||
# 运行测试并显示输出
|
||
cargo test -- --nocapture
|
||
|
||
# 运行测试并生成文档
|
||
cargo test --doc
|
||
```
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### Q: 如何添加新的错误类型?
|
||
|
||
A: 在 `error.rs` 中添加新的枚举变体,并在 `Display` 实现中添加处理:
|
||
|
||
```rust
|
||
pub enum AppError {
|
||
// ...
|
||
NewErrorType(String),
|
||
}
|
||
|
||
impl fmt::Display for AppError {
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
match self {
|
||
// ...
|
||
AppError::NewErrorType(msg) => write!(f, "新错误: {}", msg),
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Q: 如何调用 Windows API?
|
||
|
||
A: 在 Service 层直接使用 Windows 相关的 crate 或调用 Windows API:
|
||
|
||
```rust
|
||
// 使用 windows-rs crate
|
||
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
|
||
|
||
impl CursorService {
|
||
pub fn get_cursor_position(&self) -> AppResult<(i32, i32)> {
|
||
let mut pos = POINT { x: 0, y: 0 };
|
||
unsafe {
|
||
GetCursorPos(&mut pos)?;
|
||
}
|
||
Ok((pos.x, pos.y))
|
||
}
|
||
}
|
||
```
|
||
|
||
### Q: 如何处理异步操作?
|
||
|
||
A: 在 Service 层提供 async 函数,Command 层使用 async 命令:
|
||
|
||
```rust
|
||
// Service
|
||
pub async fn fetch_data(&self) -> AppResult<Data> {
|
||
// 异步操作
|
||
}
|
||
|
||
// Command
|
||
#[tauri::command]
|
||
pub async fn get_data() -> Result<Data, String> {
|
||
service.fetch_data().await.map_err(|e| e.to_string())
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 检查清单
|
||
|
||
在提交代码前,确保:
|
||
|
||
- [ ] 代码符合 Rust 格式规范(`cargo fmt --check`)
|
||
- [ ] 代码通过 clippy 检查(`cargo clippy`)
|
||
- [ ] 所有测试通过(`cargo test`)
|
||
- [ ] 编译成功(`cargo check`)
|
||
- [ ] 所有公开 API 都有文档注释
|
||
- [ ] 文档注释包含示例代码
|
||
- [ ] 错误消息使用中文
|
||
- [ ] 遵循依赖原则(上层依赖下层)
|
||
- [ ] 新增类型已导出(如需要)
|
||
- [ ] 命令已在 `lib.rs` 中注册
|
||
|
||
---
|
||
|
||
## 参考资源
|
||
|
||
- [Rust 官方文档](https://doc.rust-lang.org/)
|
||
- [Tauri 官方文档](https://tauri.app/)
|
||
- [Rust API 指南](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html)
|
||
- [Rust 命名规范](https://rust-lang.github.io/api-guidelines/naming.html)
|