Skip to main content

git_gardener/commands/
init.rs

1use crate::config::Config;
2use crate::error::{GitGardenerError, Result};
3use crate::git::GitWorktree;
4use std::fs;
5use std::path::Path;
6
7pub struct InitCommand {
8    pub force: bool,
9}
10
11impl InitCommand {
12    pub fn new(force: bool) -> Self {
13        Self { force }
14    }
15    
16    pub fn execute(&self) -> Result<()> {
17        let git_worktree = GitWorktree::new()?;
18        let repo_root = git_worktree.get_repository_root()?;
19        
20        let config_path = repo_root.join(".gardener.yml");
21        let gardener_dir = repo_root.join(".gardener");
22        let gitignore_path = repo_root.join(".gitignore");
23        
24        // 既に初期化されているかチェック
25        if config_path.exists() && !self.force {
26            return Err(GitGardenerError::Custom(
27                "git-gardener is already initialized. Use --force to reinitialize.".to_string()
28            ));
29        }
30        
31        // .gardenerディレクトリを作成
32        if !gardener_dir.exists() {
33            fs::create_dir_all(&gardener_dir)?;
34            println!("✓ Created .gardener directory");
35        }
36        
37        // .gitignoreに.gardenerを追加
38        self.update_gitignore(&gitignore_path)?;
39        
40        // .gardener.ymlファイルを作成
41        let config = Config::default();
42        config.save_to_file(&config_path)?;
43        println!("✓ Created .gardener.yml configuration file");
44        
45        println!("git-gardener initialized successfully!");
46        Ok(())
47    }
48    
49    fn update_gitignore(&self, gitignore_path: &Path) -> Result<()> {
50        let gardener_entry = ".gardener/";
51        
52        // .gitignoreファイルが存在するかチェック
53        if gitignore_path.exists() {
54            let content = fs::read_to_string(gitignore_path)?;
55            
56            // 既に.gardenerエントリがあるかチェック
57            if content.lines().any(|line| line.trim() == gardener_entry) {
58                return Ok(());
59            }
60            
61            // .gardenerエントリを追加
62            let new_content = if content.ends_with('\n') {
63                format!("{}{}\n", content, gardener_entry)
64            } else {
65                format!("{}\n{}\n", content, gardener_entry)
66            };
67            
68            fs::write(gitignore_path, new_content)?;
69        } else {
70            // .gitignoreファイルを新規作成
71            fs::write(gitignore_path, format!("{}\n", gardener_entry))?;
72        }
73        
74        println!("✓ Updated .gitignore");
75        Ok(())
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use tempfile::tempdir;
83    use std::fs;
84    use std::process::Command;
85
86    fn setup_git_repo() -> tempfile::TempDir {
87        let temp_dir = tempdir().unwrap();
88        let repo_path = temp_dir.path();
89        
90        // Git リポジトリを初期化
91        Command::new("git")
92            .args(&["init"])
93            .current_dir(repo_path)
94            .output()
95            .expect("Failed to init git repo");
96        
97        // 設定
98        Command::new("git")
99            .args(&["config", "user.name", "Test User"])
100            .current_dir(repo_path)
101            .output()
102            .unwrap();
103        
104        Command::new("git")
105            .args(&["config", "user.email", "test@example.com"])
106            .current_dir(repo_path)
107            .output()
108            .unwrap();
109        
110        // 初期ファイルとコミットを作成
111        fs::write(repo_path.join("README.md"), "# Test Repo").unwrap();
112        
113        Command::new("git")
114            .args(&["add", "."])
115            .current_dir(repo_path)
116            .output()
117            .unwrap();
118        
119        Command::new("git")
120            .args(&["commit", "-m", "Initial commit"])
121            .current_dir(repo_path)
122            .output()
123            .unwrap();
124        
125        temp_dir
126    }
127
128    #[test]
129    fn test_init_command_new_creates_instance() {
130        // What: InitCommand::newが正しくインスタンスを作成するかテスト
131        let cmd = InitCommand::new(false);
132        assert_eq!(cmd.force, false);
133        
134        let cmd = InitCommand::new(true);
135        assert_eq!(cmd.force, true);
136    }
137
138    #[test]
139    fn test_init_command_fails_without_git_repo() {
140        // What: Gitリポジトリでない場所でInitCommandが失敗するかテスト
141        let temp_dir = tempdir().unwrap();
142        std::env::set_current_dir(temp_dir.path()).unwrap();
143        
144        let cmd = InitCommand::new(false);
145        let result = cmd.execute();
146        
147        assert!(result.is_err());
148        assert!(matches!(result.unwrap_err(), GitGardenerError::NotInRepository));
149    }
150
151    #[test]
152    fn test_init_command_creates_gardener_directory() {
153        // What: initコマンドが.gardenerディレクトリを作成するかテスト
154        let temp_dir = setup_git_repo();
155        std::env::set_current_dir(temp_dir.path()).unwrap();
156        
157        let cmd = InitCommand::new(false);
158        let result = cmd.execute();
159        
160        assert!(result.is_ok());
161        
162        // .gardenerディレクトリが作成されたことを確認
163        let gardener_dir = temp_dir.path().join(".gardener");
164        assert!(gardener_dir.exists());
165        assert!(gardener_dir.is_dir());
166    }
167
168    #[test]
169    fn test_init_command_creates_config_file() {
170        // What: initコマンドが.gardener.ymlファイルを作成するかテスト
171        let temp_dir = setup_git_repo();
172        std::env::set_current_dir(temp_dir.path()).unwrap();
173        
174        let cmd = InitCommand::new(false);
175        let result = cmd.execute();
176        
177        assert!(result.is_ok());
178        
179        // .gardener.ymlファイルが作成されたことを確認
180        let config_path = temp_dir.path().join(".gardener.yml");
181        assert!(config_path.exists());
182        
183        // ファイルが有効なYAMLであることを確認
184        let config = Config::load_from_file(&config_path).unwrap();
185        assert_eq!(config.version, "1.0");
186    }
187
188    #[test]
189    fn test_init_command_updates_gitignore() {
190        // What: initコマンドが.gitignoreを正しく更新するかテスト
191        let temp_dir = setup_git_repo();
192        std::env::set_current_dir(temp_dir.path()).unwrap();
193        
194        let cmd = InitCommand::new(false);
195        let result = cmd.execute();
196        
197        assert!(result.is_ok());
198        
199        // .gitignoreファイルが作成/更新されたことを確認
200        let gitignore_path = temp_dir.path().join(".gitignore");
201        assert!(gitignore_path.exists());
202        
203        let content = fs::read_to_string(gitignore_path).unwrap();
204        assert!(content.contains(".gardener/"));
205    }
206
207    #[test]
208    fn test_init_command_updates_existing_gitignore() {
209        // What: 既存の.gitignoreファイルを正しく更新するかテスト
210        let temp_dir = setup_git_repo();
211        std::env::set_current_dir(temp_dir.path()).unwrap();
212        
213        // 既存の.gitignoreファイルを作成
214        let gitignore_path = temp_dir.path().join(".gitignore");
215        fs::write(&gitignore_path, "*.log\n/target/\n").unwrap();
216        
217        let cmd = InitCommand::new(false);
218        let result = cmd.execute();
219        
220        assert!(result.is_ok());
221        
222        let content = fs::read_to_string(gitignore_path).unwrap();
223        assert!(content.contains("*.log"));
224        assert!(content.contains("/target/"));
225        assert!(content.contains(".gardener/"));
226    }
227
228    #[test]
229    fn test_init_command_fails_when_already_initialized() {
230        // What: 既に初期化されている場合にエラーが返されるかテスト
231        let temp_dir = setup_git_repo();
232        std::env::set_current_dir(temp_dir.path()).unwrap();
233        
234        // 最初の初期化
235        let cmd = InitCommand::new(false);
236        let result = cmd.execute();
237        assert!(result.is_ok());
238        
239        // 2回目の初期化(forceフラグなし)
240        let cmd = InitCommand::new(false);
241        let result = cmd.execute();
242        
243        assert!(result.is_err());
244        assert!(matches!(result.unwrap_err(), GitGardenerError::Custom(_)));
245    }
246
247    #[test]
248    fn test_init_command_force_reinitializes() {
249        // What: --forceフラグで再初期化できるかテスト
250        let temp_dir = setup_git_repo();
251        std::env::set_current_dir(temp_dir.path()).unwrap();
252        
253        // 最初の初期化
254        let cmd = InitCommand::new(false);
255        let result = cmd.execute();
256        assert!(result.is_ok());
257        
258        // 2回目の初期化(forceフラグあり)
259        let cmd = InitCommand::new(true);
260        let result = cmd.execute();
261        
262        assert!(result.is_ok());
263    }
264}