Skip to main content

lit/commands/
add.rs

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    // Skip .lit directory
55    if file_path.starts_with(repo_root.join(".lit")) {
56        return Ok(());
57    }
58
59    // Read file content
60    let content = fs::read(file_path)
61        .map_err(|e| format!("Failed to read file {}: {}", file_path.display(), e))?;
62
63    // Create blob object
64    let blob = Blob::new(content);
65    let object = Object::Blob(blob);
66
67    // Write to object store
68    let hash = store.write(&object)?;
69
70    // Get relative path from repo root
71    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    // Add to index
78    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}