Skip to main content

git_gardener/commands/
cd.rs

1use crate::error::{GitGardenerError, Result};
2use crate::git::GitWorktree;
3
4pub struct CdCommand {
5    pub worktree: String,
6}
7
8impl CdCommand {
9    pub fn new(worktree: String) -> Self {
10        Self { worktree }
11    }
12    
13    pub fn execute(&self) -> Result<String> {
14        let git_worktree = GitWorktree::new()?;
15        let repo_root = git_worktree.get_repository_root()?;
16        
17        // @でメインワークツリーに移動
18        if self.worktree == "@" {
19            return Ok(repo_root.to_string_lossy().to_string());
20        }
21        
22        // worktreeの一覧を取得
23        let worktrees = git_worktree.list_worktrees()?;
24        
25        // 指定されたworktreeを検索(ブランチ名またはworktree名で検索)
26        let target_worktree = worktrees.iter()
27            .find(|w| w.name == self.worktree || w.branch == self.worktree)
28            .ok_or_else(|| GitGardenerError::Custom(
29                format!("Worktree '{}' not found", self.worktree)
30            ))?;
31        
32        // worktreeのパスを返す
33        Ok(target_worktree.path.to_string_lossy().to_string())
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use tempfile::tempdir;
41    use std::fs;
42    use std::process::Command;
43
44    fn setup_git_repo_with_worktree() -> tempfile::TempDir {
45        let temp_dir = tempdir().unwrap();
46        let repo_path = temp_dir.path();
47        
48        // Git リポジトリを初期化
49        Command::new("git")
50            .args(&["init"])
51            .current_dir(repo_path)
52            .output()
53            .expect("Failed to init git repo");
54        
55        // 設定
56        Command::new("git")
57            .args(&["config", "user.name", "Test User"])
58            .current_dir(repo_path)
59            .output()
60            .unwrap();
61        
62        Command::new("git")
63            .args(&["config", "user.email", "test@example.com"])
64            .current_dir(repo_path)
65            .output()
66            .unwrap();
67        
68        // 初期ファイルとコミットを作成
69        fs::write(repo_path.join("README.md"), "# Test Repo").unwrap();
70        
71        Command::new("git")
72            .args(&["add", "."])
73            .current_dir(repo_path)
74            .output()
75            .unwrap();
76        
77        Command::new("git")
78            .args(&["commit", "-m", "Initial commit"])
79            .current_dir(repo_path)
80            .output()
81            .unwrap();
82        
83        // テスト用のworktreeを作成
84        let worktree_path = repo_path.join("feature-test");
85        Command::new("git")
86            .args(&["worktree", "add", "-b", "feature-test", &worktree_path.to_string_lossy()])
87            .current_dir(repo_path)
88            .output()
89            .unwrap();
90        
91        temp_dir
92    }
93
94    #[test]
95    fn test_cd_command_new_creates_instance() {
96        // What: CdCommand::newが正しくインスタンスを作成するかテスト
97        let cmd = CdCommand::new("test-branch".to_string());
98        assert_eq!(cmd.worktree, "test-branch");
99    }
100
101    #[test]
102    fn test_cd_command_fails_without_git_repo() {
103        // What: Gitリポジトリでない場所でCdCommandが失敗するかテスト
104        let temp_dir = tempdir().unwrap();
105        std::env::set_current_dir(temp_dir.path()).unwrap();
106        
107        let cmd = CdCommand::new("test".to_string());
108        let result = cmd.execute();
109        
110        assert!(result.is_err());
111        assert!(matches!(result.unwrap_err(), GitGardenerError::NotInRepository));
112    }
113
114    #[test]
115    fn test_cd_command_returns_main_worktree_for_at_symbol() {
116        // What: @記号でメインワークツリーのパスを返すかテスト
117        let temp_dir = setup_git_repo_with_worktree();
118        std::env::set_current_dir(temp_dir.path()).unwrap();
119        
120        let cmd = CdCommand::new("@".to_string());
121        let result = cmd.execute();
122        
123        assert!(result.is_ok());
124        let path = result.unwrap();
125        // メインリポジトリのパスが返されることを確認
126        assert!(path.contains(temp_dir.path().to_str().unwrap()));
127    }
128
129    #[test]
130    fn test_cd_command_finds_worktree_by_branch_name() {
131        // What: ブランチ名でワークツリーを見つけられるかテスト
132        let temp_dir = setup_git_repo_with_worktree();
133        std::env::set_current_dir(temp_dir.path()).unwrap();
134        
135        let cmd = CdCommand::new("feature-test".to_string());
136        let result = cmd.execute();
137        
138        assert!(result.is_ok());
139        let path = result.unwrap();
140        assert!(path.contains("feature-test"));
141    }
142
143    #[test]
144    fn test_cd_command_fails_for_nonexistent_worktree() {
145        // What: 存在しないワークツリーに対して失敗するかテスト
146        let temp_dir = setup_git_repo_with_worktree();
147        std::env::set_current_dir(temp_dir.path()).unwrap();
148        
149        let cmd = CdCommand::new("nonexistent-worktree".to_string());
150        let result = cmd.execute();
151        
152        assert!(result.is_err());
153        let error_msg = format!("{}", result.unwrap_err());
154        assert!(error_msg.contains("not found"));
155    }
156}