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