diff --git a/src/commands/add.rs b/src/commands/add.rs index 2033f3a..7c7f430 100644 --- a/src/commands/add.rs +++ b/src/commands/add.rs @@ -5,6 +5,7 @@ use colored::Colorize; use crate::commands::status; use crate::models::index::FileMetaData; use crate::models::*; +use crate::utils::path_ext::PathExt; use crate::utils::util; /// add是对index的操作,不会对工作区产生影响 @@ -40,7 +41,7 @@ pub fn add(raw_paths: Vec, all: bool, mut update: bool) { fn add_a_file(file: &Path, index: &mut Index) { let workdir = util::get_working_dir().unwrap(); - if !util::is_sub_path(file, &workdir) { + if !file.is_sub_to(&workdir) { //文件不在工作区内 println!("fatal: '{}' is outside workdir at '{}'", file.display(), workdir.display()); return; @@ -51,7 +52,7 @@ fn add_a_file(file: &Path, index: &mut Index) { return; } - let rel_path = util::get_relative_path(file); + let rel_path = file.to_relative(); if !file.exists() { //文件被删除 index.remove(file); diff --git a/src/commands/restore.rs b/src/commands/restore.rs index abf8cd1..309759e 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -4,6 +4,7 @@ use std::{ path::PathBuf, }; +use crate::utils::path_ext::PathExt; use crate::{ models::*, utils::{store, util}, @@ -23,7 +24,7 @@ fn get_worktree_deleted_files_in_filters( .iter() .filter(|(path, _)| { assert!(path.is_absolute()); // - !path.exists() && util::include_in_paths(path, filters) + !path.exists() && path.include_in(filters) }) .map(|(path, _)| path.clone()) .collect() //HashSet自动去重 @@ -39,7 +40,7 @@ fn get_index_deleted_files_in_filters( .iter() .filter(|(path, _)| { assert!(path.is_absolute()); - !index.contains(path) && util::include_in_paths(path, filters) + !index.contains(path) && path.include_in(filters) }) .map(|(path, _)| path.clone()) .collect() //HashSet自动去重 @@ -58,7 +59,7 @@ fn preprocess_filters(filters: Option<&Vec>) -> Vec { fn preprocess_blobs(blobs: &Vec<(PathBuf, Hash)>) -> HashMap { blobs // 转为绝对路径 //TODO tree改变路径表示方式后,这里需要修改 .iter() - .map(|(path, hash)| (util::to_workdir_absolute_path(path), hash.clone())) + .map(|(path, hash)| (path.to_absolute_workdir(), hash.clone())) .collect() //to HashMap } diff --git a/src/commands/status.rs b/src/commands/status.rs index 9ea6a3a..62e4595 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -1,4 +1,5 @@ use crate::models::head; +use crate::utils::path_ext::PathExt; use crate::{ models::{Blob, Commit, Index}, utils::util, @@ -47,7 +48,7 @@ impl Changes { [&mut change.new, &mut change.modified, &mut change.deleted] .iter_mut() .for_each(|paths| { - **paths = util::map(&**paths, |p| util::to_workdir_absolute_path(p)); + **paths = util::map(&**paths, |p| p.to_absolute_workdir()); }); change } @@ -80,7 +81,7 @@ pub fn changes_to_be_committed() -> Changes { let tracked_files = index .get_tracked_files() .iter() - .map(|f| util::to_workdir_relative_path(f)) + .map(|f| f.to_relative_workdir()) .collect::>(); if head_hash == "" { // 初始提交 @@ -96,7 +97,7 @@ pub fn changes_to_be_committed() -> Changes { for (tree_file, blob_hash) in tree_files.iter() { let index_file = index_files.iter().find(|&f| f == tree_file); if let Some(index_file) = index_file { - let index_path = util::to_workdir_absolute_path(index_file); + let index_path = index_file.to_absolute_workdir(); if !index.verify_hash(&index_path, blob_hash) { change.modified.push(tree_file.clone()); } @@ -119,12 +120,12 @@ pub fn changes_to_be_staged() -> Changes { let index = Index::get_instance(); for file in index.get_tracked_files() { if !file.exists() { - change.deleted.push(util::to_workdir_relative_path(&file)); + change.deleted.push(file.to_relative_workdir()); } else if index.is_modified(&file) { // 若文件元数据被修改,才需要比较暂存区与文件的hash来判别内容修改 let dry_blob = Blob::dry_new(util::read_workfile(&file)); if !index.verify_hash(&file, &dry_blob.get_hash()) { - change.modified.push(util::to_workdir_relative_path(&file)); + change.modified.push(file.to_relative_workdir()); } } } @@ -132,7 +133,7 @@ pub fn changes_to_be_staged() -> Changes { for file in files { if !index.tracked(&file) { //文件未被跟踪 - change.new.push(util::to_workdir_relative_path(&file)); + change.new.push(file.to_relative_workdir()); } } change diff --git a/src/models/index.rs b/src/models/index.rs index f0b2aee..84ddcae 100644 --- a/src/models/index.rs +++ b/src/models/index.rs @@ -1,3 +1,4 @@ +use crate::utils::path_ext::PathExt; use crate::{models::*, utils::util}; use once_cell::unsync::Lazy; use serde::{Deserialize, Serialize}; @@ -76,7 +77,7 @@ impl Index { /// 预处理路径,统一形式为绝对路径 fn preprocess(path: &Path) -> PathBuf { - util::get_absolute_path(&path) + path.to_absolute() } // 添加文件 diff --git a/src/models/tree.rs b/src/models/tree.rs index 090fafc..f5e7d0c 100644 --- a/src/models/tree.rs +++ b/src/models/tree.rs @@ -2,6 +2,7 @@ use std::{collections::HashSet, path::PathBuf}; use serde::{Deserialize, Serialize}; +use crate::utils::PathExt; use crate::utils::{store, util}; use super::{Hash, Index}; @@ -41,7 +42,7 @@ fn store_path_to_tree(index: &Index, current_root: PathBuf) -> Tree { let path_entries: Vec = index .get_tracked_files() .iter() - .map(|file| util::to_workdir_relative_path(file)) + .map(|file| file.to_relative_workdir()) .filter(|path| path.starts_with(¤t_root)) .collect(); for path in path_entries.iter() { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 381e0a1..6836d24 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,5 @@ +pub mod path_ext; +pub use path_ext::PathExt; pub mod store; pub mod test; pub mod util; diff --git a/src/utils/path_ext.rs b/src/utils/path_ext.rs new file mode 100644 index 0000000..a4242bb --- /dev/null +++ b/src/utils/path_ext.rs @@ -0,0 +1,56 @@ +use crate::utils::util; +use std::path::{Path, PathBuf}; + +/** +Path的扩展 基于util 为了解耦,不要再util中使用PathExt + */ +pub trait PathExt { + fn to_absolute(&self) -> PathBuf; + fn to_absolute_workdir(&self) -> PathBuf; + fn to_relative(&self) -> PathBuf; + fn to_relative_workdir(&self) -> PathBuf; + fn is_sub_to(&self, parent: &PathBuf) -> bool; + fn include_in(&self, paths: U) -> bool + where + T: AsRef, + U: IntoIterator; +} +/* +在 Rust 中,当你调用一个方法时,Rust 会尝试自动解引用和自动引用(auto-deref and auto-ref)来匹配方法签名。 +如果有一个为 Path 实现的方法,你可以在 PathBuf、&PathBuf、&&PathBuf 等上调用这个方法,Rust 会自动进行必要的解引用。 + */ +impl PathExt for Path { + /// 转换为绝对路径 + fn to_absolute(&self) -> PathBuf { + util::get_absolute_path(&self) + } + + /// 转换为绝对路径(from workdir相对路径) + fn to_absolute_workdir(&self) -> PathBuf { + util::to_workdir_absolute_path(&self) + } + + /// 转换为相对路径(to cur_dir) + fn to_relative(&self) -> PathBuf { + util::get_relative_path(&self) + } + + /// 转换为相对路径(to workdir) + fn to_relative_workdir(&self) -> PathBuf { + util::to_workdir_relative_path(&self) + } + + /// 从字符串角度判断path是否是parent的子路径(不检测存在性) + fn is_sub_to(&self, parent: &PathBuf) -> bool { + util::is_sub_path(&self, parent) + } + + /// 判断是否在paths中(包括子目录),不检查存在 + fn include_in(&self, paths: U) -> bool + where + T: AsRef, + U: IntoIterator, + { + util::include_in_paths(&self, paths) + } +} diff --git a/src/utils/test.rs b/src/utils/test.rs index 0223354..30a0867 100644 --- a/src/utils/test.rs +++ b/src/utils/test.rs @@ -8,6 +8,7 @@ use std::{ }; use crate::models::Index; +use crate::utils::PathExt; // 执行测试的储存库 use super::util; @@ -114,7 +115,7 @@ pub fn ensure_no_file(path: &Path) { /** 列出子文件夹 */ pub fn list_subdir(path: &Path) -> io::Result> { let mut files = Vec::new(); - let path = util::get_absolute_path(path); + let path = path.to_absolute(); if path.is_dir() { for entry in fs::read_dir(path)? { let entry = entry?;