rustic_git/commands/
config.rs

1use crate::utils::git;
2use crate::{Repository, Result};
3
4/// Repository configuration manager
5///
6/// Provides methods for getting and setting git configuration values
7/// for a specific repository.
8pub struct RepoConfig<'a> {
9    repo: &'a Repository,
10}
11
12impl<'a> RepoConfig<'a> {
13    /// Create a new RepoConfig instance
14    pub(crate) fn new(repo: &'a Repository) -> Self {
15        Self { repo }
16    }
17
18    /// Configure git user name and email for this repository
19    ///
20    /// This is a convenience method that sets both user.name and user.email
21    /// configuration values.
22    ///
23    /// # Arguments
24    ///
25    /// * `name` - The user's full name
26    /// * `email` - The user's email address
27    ///
28    /// # Example
29    ///
30    /// ```rust
31    /// use rustic_git::Repository;
32    /// use std::{env, fs};
33    ///
34    /// let test_path = env::temp_dir().join("config_set_user_test");
35    /// if test_path.exists() {
36    ///     fs::remove_dir_all(&test_path).unwrap();
37    /// }
38    ///
39    /// let repo = Repository::init(&test_path, false)?;
40    /// repo.config().set_user("John Doe", "john@example.com")?;
41    ///
42    /// // Clean up
43    /// fs::remove_dir_all(&test_path).unwrap();
44    /// # Ok::<(), rustic_git::GitError>(())
45    /// ```
46    pub fn set_user(&self, name: &str, email: &str) -> Result<()> {
47        self.set("user.name", name)?;
48        self.set("user.email", email)?;
49        Ok(())
50    }
51
52    /// Get the current git user configuration
53    ///
54    /// Returns a tuple of (name, email) from the repository configuration.
55    ///
56    /// # Returns
57    ///
58    /// A tuple containing the user name and email, or an error if either
59    /// configuration value is not set.
60    pub fn get_user(&self) -> Result<(String, String)> {
61        let name = self.get("user.name")?;
62        let email = self.get("user.email")?;
63        Ok((name, email))
64    }
65
66    /// Set a git configuration value for this repository
67    ///
68    /// # Arguments
69    ///
70    /// * `key` - The configuration key (e.g., "user.name", "core.autocrlf")
71    /// * `value` - The value to set
72    ///
73    /// # Example
74    ///
75    /// ```rust
76    /// use rustic_git::Repository;
77    /// use std::{env, fs};
78    ///
79    /// let test_path = env::temp_dir().join("config_set_test");
80    /// if test_path.exists() {
81    ///     fs::remove_dir_all(&test_path).unwrap();
82    /// }
83    ///
84    /// let repo = Repository::init(&test_path, false)?;
85    /// repo.config().set("core.autocrlf", "false")?;
86    /// repo.config().set("user.name", "Jane Doe")?;
87    ///
88    /// // Clean up
89    /// fs::remove_dir_all(&test_path).unwrap();
90    /// # Ok::<(), rustic_git::GitError>(())
91    /// ```
92    pub fn set(&self, key: &str, value: &str) -> Result<()> {
93        git(&["config", key, value], Some(self.repo.repo_path()))?;
94        Ok(())
95    }
96
97    /// Get a git configuration value from this repository
98    ///
99    /// # Arguments
100    ///
101    /// * `key` - The configuration key to retrieve
102    ///
103    /// # Returns
104    ///
105    /// The configuration value as a string, or an error if the key is not found.
106    ///
107    /// # Example
108    ///
109    /// ```rust
110    /// use rustic_git::Repository;
111    /// use std::{env, fs};
112    ///
113    /// let test_path = env::temp_dir().join("config_get_test");
114    /// if test_path.exists() {
115    ///     fs::remove_dir_all(&test_path).unwrap();
116    /// }
117    ///
118    /// let repo = Repository::init(&test_path, false)?;
119    /// repo.config().set("user.name", "Jane Doe")?;
120    /// let name = repo.config().get("user.name")?;
121    /// assert_eq!(name, "Jane Doe");
122    ///
123    /// // Clean up
124    /// fs::remove_dir_all(&test_path).unwrap();
125    /// # Ok::<(), rustic_git::GitError>(())
126    /// ```
127    pub fn get(&self, key: &str) -> Result<String> {
128        git(&["config", key], Some(self.repo.repo_path())).map(|s| s.trim().to_string())
129    }
130
131    /// Remove a git configuration value from this repository
132    ///
133    /// # Arguments
134    ///
135    /// * `key` - The configuration key to remove
136    ///
137    /// # Example
138    ///
139    /// ```rust
140    /// use rustic_git::Repository;
141    /// use std::{env, fs};
142    ///
143    /// let test_path = env::temp_dir().join("config_unset_test");
144    /// if test_path.exists() {
145    ///     fs::remove_dir_all(&test_path).unwrap();
146    /// }
147    ///
148    /// let repo = Repository::init(&test_path, false)?;
149    /// repo.config().set("test.value", "temporary")?;
150    /// repo.config().unset("test.value")?;
151    ///
152    /// // Clean up
153    /// fs::remove_dir_all(&test_path).unwrap();
154    /// # Ok::<(), rustic_git::GitError>(())
155    /// ```
156    pub fn unset(&self, key: &str) -> Result<()> {
157        git(&["config", "--unset", key], Some(self.repo.repo_path()))?;
158        Ok(())
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165    use std::env;
166    use std::fs;
167
168    #[test]
169    fn test_config_set_and_get_user() {
170        let test_path = env::temp_dir().join("test_config_user");
171
172        // Clean up if exists
173        if test_path.exists() {
174            fs::remove_dir_all(&test_path).unwrap();
175        }
176
177        let repo = Repository::init(&test_path, false).unwrap();
178
179        // Set user configuration
180        repo.config()
181            .set_user("Test User", "test@example.com")
182            .unwrap();
183
184        // Get user configuration
185        let (name, email) = repo.config().get_user().unwrap();
186        assert_eq!(name, "Test User");
187        assert_eq!(email, "test@example.com");
188
189        // Clean up
190        fs::remove_dir_all(&test_path).unwrap();
191    }
192
193    #[test]
194    fn test_config_set_and_get_generic() {
195        let test_path = env::temp_dir().join("test_config_generic");
196
197        // Clean up if exists
198        if test_path.exists() {
199            fs::remove_dir_all(&test_path).unwrap();
200        }
201
202        let repo = Repository::init(&test_path, false).unwrap();
203
204        // Set generic configuration
205        repo.config().set("core.autocrlf", "false").unwrap();
206        repo.config().set("user.name", "Generic User").unwrap();
207
208        // Get generic configuration
209        let autocrlf = repo.config().get("core.autocrlf").unwrap();
210        let name = repo.config().get("user.name").unwrap();
211
212        assert_eq!(autocrlf, "false");
213        assert_eq!(name, "Generic User");
214
215        // Clean up
216        fs::remove_dir_all(&test_path).unwrap();
217    }
218
219    #[test]
220    fn test_config_unset() {
221        let test_path = env::temp_dir().join("test_config_unset");
222
223        // Clean up if exists
224        if test_path.exists() {
225            fs::remove_dir_all(&test_path).unwrap();
226        }
227
228        let repo = Repository::init(&test_path, false).unwrap();
229
230        // Set a test value
231        repo.config().set("test.temporary", "value").unwrap();
232        let value = repo.config().get("test.temporary").unwrap();
233        assert_eq!(value, "value");
234
235        // Unset the value
236        repo.config().unset("test.temporary").unwrap();
237
238        // Verify it's gone (should return error)
239        let result = repo.config().get("test.temporary");
240        assert!(result.is_err());
241
242        // Clean up
243        fs::remove_dir_all(&test_path).unwrap();
244    }
245
246    #[test]
247    fn test_config_get_nonexistent_key() {
248        let test_path = env::temp_dir().join("test_config_nonexistent");
249
250        // Clean up if exists
251        if test_path.exists() {
252            fs::remove_dir_all(&test_path).unwrap();
253        }
254
255        let repo = Repository::init(&test_path, false).unwrap();
256
257        // Try to get a non-existent key
258        let result = repo.config().get("nonexistent.key");
259        assert!(result.is_err());
260
261        // Clean up
262        fs::remove_dir_all(&test_path).unwrap();
263    }
264
265    #[test]
266    fn test_config_integration_with_commit() {
267        let test_path = env::temp_dir().join("test_config_commit_integration");
268
269        // Clean up if exists
270        if test_path.exists() {
271            fs::remove_dir_all(&test_path).unwrap();
272        }
273
274        let repo = Repository::init(&test_path, false).unwrap();
275
276        // Configure user for commits
277        repo.config()
278            .set_user("Integration Test", "integration@example.com")
279            .unwrap();
280
281        // Create a file and commit
282        std::fs::write(test_path.join("test.txt"), "test content").unwrap();
283        repo.add(&["test.txt"]).unwrap();
284        let hash = repo.commit("Test commit with config API").unwrap();
285
286        // Verify commit was created successfully
287        assert!(!hash.as_str().is_empty());
288
289        // Clean up
290        fs::remove_dir_all(&test_path).unwrap();
291    }
292}