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}