Skip to main content

syncbox/sync/
types.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3use std::time::SystemTime;
4use crate::{cli, config};
5use super::file_ops::compute_blake3_hash;
6use std::sync::Arc;
7
8// ==============================================
9// 公共类型定义
10// ==============================================
11
12#[derive(Debug, Clone)]
13pub struct FileInfo {
14    // 文件目录
15    pub path: Arc<Path>,
16    // 系统时间
17    pub mtime: SystemTime,
18    // 文件大小
19    pub size: u64,
20    // 存储 BLAKE3 哈希值
21    pub blake3_hash: Option<[u8; 32]>,
22}
23
24impl FileInfo {
25    /// 从指定路径构造一个 `FileInfo` 实例。
26    ///
27    /// # 参数
28    /// * `path` - 要读取的文件或目录路径。
29    /// * `compute_hash` - 是否计算文件内容的 BLAKE3 哈希值(仅对文件有效)。
30    ///
31    /// # 返回
32    /// * `Ok(FileInfo)` - 成功获取文件元数据。
33    /// * `Err(std::io::Error)` - 读取文件元数据或计算哈希失败。
34    ///
35    /// # 注意
36    /// 若 `compute_hash` 为 `true` 且路径指向目录,哈希值将为 `None`。
37    pub fn from_path(path: &Path, compute_hash: bool) -> std::io::Result<Self> {
38        let metadata = fs::metadata(path)?;
39        let blake3_hash = if compute_hash && metadata.is_file() {
40            Some(compute_blake3_hash(path)?)
41        } else {
42            None
43        };
44        Ok(FileInfo {
45            path: Arc::from(path), // 👈 关键修改:从 &Path 创建 Arc<Path>
46            mtime: metadata.modified()?,
47            size: metadata.len(),
48            blake3_hash,
49        })
50    }
51
52    /// 判断当前文件是否比目标文件“更新”。
53    ///
54    /// 默认同步策略依据:修改时间(mtime)或文件大小。
55    ///
56    /// # 返回
57    /// * `true` - 当前文件修改时间更新,或大小不同,应被同步。
58    /// * `false` - 目标文件更新或完全一致,无需同步。
59    pub fn is_newer_than(&self, target: &Self) -> bool {
60        self.mtime > target.mtime || self.size != target.size
61    }
62
63    /// 判断当前文件与目标文件内容是否完全一致。
64    ///
65    /// 用于启用 `--checksum` 时的精确同步策略。
66    ///
67    /// # 返回
68    /// * `true` - 文件大小和 BLAKE3 哈希值均相同。
69    /// * `false` - 大小或哈希任一不同。
70    ///
71    /// # Panics
72    /// 若任一文件未计算哈希(`blake3_hash` 为 `None`),行为未定义(应确保调用前已计算)。
73    pub fn content_eq(&self, other: &Self) -> bool {
74        self.size == other.size && self.blake3_hash == other.blake3_hash
75    }
76}
77
78/// 定义统一的同步参数结构
79#[derive(Debug, Clone)]
80pub struct SyncParameters {
81    /// 源目录
82    pub source: PathBuf,
83    /// 目标目录
84    pub target: PathBuf,
85    /// 试运行模式
86    pub dry_run: bool,
87    /// 是否使用校验和比较
88    pub checksum: bool,
89    /// 排除同步规则列表
90    pub excludes: Vec<String>,
91    /// 是否删除目标额外文件
92    pub delete_extra: bool,
93    /// 排除目标目录删除列表
94    pub delete_excludes: Vec<String>,
95    /// 是否显示详细操作列表
96    pub detail: bool,
97}
98
99/// 实现从不同来源转换为统一参数
100impl From<&cli::Command> for SyncParameters {
101    fn from(cmd: &cli::Command) -> Self {
102        match cmd {
103            cli::Command::Sync {
104                source,
105                target,
106                dry_run,
107                checksum,
108                delete,
109                exclude,
110                delete_exclude,
111                detail,
112            } => Self {
113                source: source.clone(),
114                target: target.clone(),
115                dry_run: *dry_run,
116                checksum: *checksum,
117                excludes: exclude.clone(),
118                delete_extra: *delete,
119                delete_excludes: delete_exclude.clone(),
120                detail: *detail,
121            },
122            cli::Command::Run {
123                name: _,
124                config: _,
125                dry_run,
126                checksum,
127                detail,
128            } => {
129                // 这里只是占位,实际会在加载配置后覆盖
130                Self {
131                    source: PathBuf::new(),
132                    target: PathBuf::new(),
133                    dry_run: *dry_run,
134                    checksum: *checksum,
135                    excludes: Vec::new(),
136                    delete_extra: false,
137                    delete_excludes: Vec::new(),
138                    detail: *detail,
139                }
140            }
141            cli::Command::Watch {
142                name: _,
143                config: _,
144                delay: _,
145                checksum,
146                dry_run,
147                detail,
148            } => Self {
149                source: PathBuf::new(),
150                target: PathBuf::new(),
151                dry_run: *dry_run,
152                checksum: *checksum,
153                excludes: Vec::new(),
154                delete_extra: false,
155                delete_excludes: Vec::new(),
156                detail: *detail,
157            }
158        }
159    }
160}
161
162/// 从配置任务转换
163impl From<&config::SyncTask> for SyncParameters {
164    fn from(task: &config::SyncTask) -> Self {
165        Self {
166            source: task.source.clone(),
167            target: task.target.clone(),
168            dry_run: false,  // 由命令行参数决定
169            checksum: false, // 由命令行参数决定
170            excludes: task.exclude.clone(),
171            delete_extra: task.delete_extra,
172            delete_excludes: task.delete_extra_exclude.clone(),
173            detail: false,
174        }
175    }
176}