1use crate::core::{find_repo_root, Blob, Object};
2use crate::response::AddResponse;
3use crate::storage::{Index, ObjectStore};
4use std::fs;
5use std::path::{Path, PathBuf};
6use walkdir::WalkDir;
7
8pub fn execute(files: Vec<String>) -> Result<AddResponse, crate::errors::LitError> {
9 let repo_root = find_repo_root()?;
10 let store = ObjectStore::new(&repo_root);
11 let mut index = Index::load(&repo_root)?;
12
13 let mut total_added = 0usize;
14
15 for file_pattern in &files {
16 if file_pattern == "." {
17 total_added += add_directory(&repo_root, &repo_root, &store, &mut index)?;
18 } else {
19 let file_path = PathBuf::from(file_pattern);
20 let full_path = if file_path.is_absolute() {
21 file_path
22 } else {
23 std::env::current_dir()
24 .map_err(|e| format!("Failed to get current directory: {}", e))?
25 .join(&file_path)
26 };
27
28 if !full_path.exists() {
29 return Err(format!("File not found: {}", file_pattern).into());
30 }
31
32 if full_path.is_dir() {
33 total_added += add_directory(&repo_root, &full_path, &store, &mut index)?;
34 } else {
35 add_file(&repo_root, &full_path, &store, &mut index)?;
36 total_added += 1;
37 }
38 }
39 }
40
41 index.save(&repo_root)?;
42
43 Ok(AddResponse {
44 files_added: total_added,
45 })
46}
47
48fn add_file(
49 repo_root: &Path,
50 file_path: &Path,
51 store: &ObjectStore,
52 index: &mut Index,
53) -> Result<(), crate::errors::LitError> {
54 if file_path.starts_with(repo_root.join(".lit")) {
56 return Ok(());
57 }
58
59 let content = fs::read(file_path)
61 .map_err(|e| format!("Failed to read file {}: {}", file_path.display(), e))?;
62
63 let blob = Blob::new(content);
65 let object = Object::Blob(blob);
66
67 let hash = store.write(&object)?;
69
70 let rel_path = file_path
72 .strip_prefix(repo_root)
73 .map_err(|e| format!("Path error: {}", e))?
74 .to_string_lossy()
75 .replace('\\', "/");
76
77 index.add(rel_path, hash.to_string(), "100644".to_string());
79
80 Ok(())
81}
82
83fn add_directory(
84 repo_root: &Path,
85 dir_path: &Path,
86 store: &ObjectStore,
87 index: &mut Index,
88) -> Result<usize, crate::errors::LitError> {
89 let mut count = 0usize;
90 for entry in WalkDir::new(dir_path)
91 .into_iter()
92 .filter_entry(|e| e.depth() == 0 || !is_hidden(e))
93 {
94 let entry = entry.map_err(|e| format!("Failed to read directory: {}", e))?;
95
96 if entry.file_type().is_file() {
97 add_file(repo_root, entry.path(), store, index)?;
98 count += 1;
99 }
100 }
101
102 Ok(count)
103}
104
105fn is_hidden(entry: &walkdir::DirEntry) -> bool {
106 entry
107 .file_name()
108 .to_str()
109 .map(|s| s.starts_with('.'))
110 .unwrap_or(false)
111}