diff --git a/src/models/blob.rs b/src/models/blob.rs index 9370d3c..98e57f0 100644 --- a/src/models/blob.rs +++ b/src/models/blob.rs @@ -17,7 +17,7 @@ pub struct Blob { impl Blob { /// 从源文件新建blob对象,并直接保存到/objects/中 pub fn new(file: &Path) -> Blob { - let data = fs::read_to_string(file).expect("无法读取文件"); + let data = fs::read_to_string(file).expect(format!("无法读取文件:{:?}", file).as_str()); let hash = calc_hash(&data); let blob = Blob { hash, data }; blob.save(); diff --git a/src/models/commit.rs b/src/models/commit.rs index 22ef6f3..f9cdbc9 100644 --- a/src/models/commit.rs +++ b/src/models/commit.rs @@ -21,7 +21,7 @@ pub struct Commit { impl Commit { pub fn new(index: &Index, parent: Vec, message: String) -> Commit { - let tree = Tree::new(index); + let mut tree = Tree::new(index); let tree_hash = tree.save(); Commit { hash: "".to_string(), diff --git a/src/models/tree.rs b/src/models/tree.rs index 4d32416..22b0271 100644 --- a/src/models/tree.rs +++ b/src/models/tree.rs @@ -1,6 +1,8 @@ +use std::{collections::HashMap, path::PathBuf}; + use serde::{Deserialize, Serialize}; -use crate::store; +use crate::{store, utils::util}; use super::{index::Index, object::Hash}; /*Tree @@ -21,14 +23,76 @@ pub struct Tree { pub entries: Vec, } -impl Tree { - pub fn new(index: &Index) -> Tree { - // XXX wait for index to be implemented - Tree { - hash: "".to_string(), - entries: Vec::new(), +/** 将文件列表保存为Tree Object,并返回最上层的Tree */ +fn store_path_to_tree(path_entries: &Vec, current_root: PathBuf) -> Tree { + let get_blob_entry = |path: &PathBuf| { + let file_path = util::get_working_dir().unwrap().join(path); + let blob = super::blob::Blob::new(&file_path.clone()); + let mode = util::get_file_mode(&path); + let filename = path.file_name().unwrap().to_str().unwrap().to_string(); + let entry = TreeEntry { + filemode: (String::from("blob"), mode), + object_hash: blob.get_hash(), + name: filename, }; - unimplemented!("Tree::new"); + entry + }; + let mut tree = Tree { + hash: "".to_string(), + entries: Vec::new(), + }; + let mut processed_path: HashMap = HashMap::new(); + for path in path_entries.iter() { + // 判断是不是直接在根目录下 + let in_path = path.parent().unwrap() == current_root; + // 一定是文件,不会是目录 + if in_path { + let entry = get_blob_entry(path); + tree.entries.push(entry); + } else { + if path.components().count() == 1 { + continue; + } + // 拿到下一级别目录 + let process_path = path + .components() + .nth(0) + .unwrap() + .as_os_str() + .to_str() + .unwrap(); + if processed_path + .insert(process_path.to_string(), true) + .is_some() + { + continue; + } + + let sub_tree = store_path_to_tree(path_entries, process_path.into()); + let mode = util::get_file_mode(&util::get_working_dir().unwrap().join(process_path)); + tree.entries.push(TreeEntry { + filemode: (String::from("tree"), mode), + object_hash: sub_tree.get_hash(), + name: process_path.to_string(), + }); + } + } + tree.save(); + tree +} + +impl Tree { + pub fn get_hash(&self) -> String { + self.hash.clone() + } + pub fn new(index: &Index) -> Tree { + let file_entries: Vec = index + .get_tracked_files() + .iter_mut() + .map(|file| util::to_root_relative_path(file)) + .collect(); + + store_path_to_tree(&file_entries, "".to_string().into()) } pub fn load(hash: &String) -> Tree { @@ -39,10 +103,64 @@ impl Tree { tree } - pub fn save(&self) -> String { + pub fn save(&mut self) -> String { let s = store::Store::new(); let tree_data = serde_json::to_string_pretty(&self).unwrap(); let hash = s.save(&tree_data); + self.hash = hash.clone(); hash } } + +#[cfg(test)] +mod test { + use std::path::PathBuf; + + use crate::models::blob::Blob; + use crate::models::index::FileMetaData; + use crate::utils::util; + #[test] + fn test_new() { + util::setup_test_with_clean_mit(); + let mut index = super::Index::new(); + for test_file in vec!["b.txt", "mit_src/a.txt"] { + let test_file = PathBuf::from(test_file); + util::ensure_test_file(&test_file, None); + index.add( + test_file.clone(), + FileMetaData::new(&Blob::new(&test_file), &test_file), + ); + index.add( + test_file.clone(), + FileMetaData::new(&Blob::new(&test_file), &test_file), + ); + } + + let tree = super::Tree::new(&index); + assert!(tree.entries.len() == 2); + assert!(tree.hash.len() != 0); + } + + #[test] + fn test_load() { + util::setup_test_with_clean_mit(); + let mut index = super::Index::new(); + let test_files = vec!["b.txt", "mit_src/a.txt"]; + for test_file in test_files.clone() { + let test_file = PathBuf::from(test_file); + util::ensure_test_file(&test_file, None); + index.add( + test_file.clone(), + FileMetaData::new(&Blob::new(&test_file), &test_file), + ); + } + + let tree = super::Tree::new(&index); + let tree_hash = tree.get_hash(); + + let loaded_tree = super::Tree::load(&tree_hash); + assert!(loaded_tree.entries.len() == tree.entries.len()); + assert!(tree.entries[0].name == loaded_tree.entries[0].name); + assert!(tree.entries[1].name == loaded_tree.entries[1].name); + } +}