• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MitMaro / git-interactive-rebase-tool / 15656777377

14 Jun 2025 10:36PM UTC coverage: 97.566% (-0.02%) from 97.589%
15656777377

Pull #978

github

web-flow
Merge c1ecfa05d into 4d16b296a
Pull Request #978: refactor: remove uses of `Option` in `new_with_config()`

148 of 152 new or added lines in 14 files covered. (97.37%)

1 existing line in 1 file now uncovered.

4930 of 5053 relevant lines covered (97.57%)

2.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/src/config/git_config.rs
1
use std::env;
2

3
use crate::{
4
        config::{
5
                ConfigError,
6
                utils::{get_optional_string, get_unsigned_integer, git_diff_renames},
7
        },
8
        git::Config,
9
};
10

11
fn get_default_editor() -> String {
1✔
12
        ["GIT_EDITOR", "VISUAL", "EDITOR"]
1✔
13
                .into_iter()
14
                .find_map(|var| env::var(var).ok())
3✔
15
                .unwrap_or_else(|| String::from("vi"))
3✔
16
}
17

18
/// XXX: this logic is duplicated in [`get_default_editor`]
19
fn get_default_editor_with_config(git_config: &Config) -> Result<String, ConfigError> {
1✔
20
        let editor = if let Ok(editor) = env::var("GIT_EDITOR") {
2✔
NEW
21
                editor
×
22
        } else if let Some(editor) = get_optional_string(git_config, "core.editor")? {
4✔
23
                editor
1✔
24
        } else if let Ok(editor) = env::var("VISUAL") {
3✔
NEW
25
                editor
×
26
        } else if let Ok(editor) = env::var("EDITOR") {
2✔
NEW
27
                editor
×
28
        } else {
29
                String::from("vi")
2✔
30
        };
31

32
        Ok(editor)
1✔
33
}
34

35
/// Represents the git configuration options.
36
#[derive(Clone, Debug)]
37
#[non_exhaustive]
38
pub(crate) struct GitConfig {
39
        /// The Git comment character, from [`core.commentChar`](
40
        ///     https://git-scm.com/docs/git-config#Documentation/git-config.txt-corecommentChar
41
        /// ).
42
        pub(crate) comment_char: String,
43
        /// Number of context lines, from [`diff.context`](
44
        ///     https://git-scm.com/docs/diff-config/#Documentation/diff-config.txt-diffcontext
45
        /// ).
46
        pub(crate) diff_context: u32,
47
        /// Number of interhunk lines, from [`diff.interhunk_lines`](
48
        ///     https://git-scm.com/docs/diff-config/#Documentation/diff-config.txt-diffinterHunkContext
49
        /// ).
50
        pub(crate) diff_interhunk_lines: u32,
51
        /// The limit for detecting renames, from [`diff.renameLimit`](
52
        ///     https://git-scm.com/docs/diff-config/#Documentation/diff-config.txt-diffrenameLimit
53
        /// ).
54
        pub(crate) diff_rename_limit: u32,
55
        /// If to detect renames, from [`diff.renames`](
56
        ///     https://git-scm.com/docs/diff-config/#Documentation/diff-config.txt-diffrenames
57
        /// ).
58
        pub(crate) diff_renames: bool,
59
        /// If to detect copies, from [`diff.renames`](
60
        ///     https://git-scm.com/docs/diff-config/#Documentation/diff-config.txt-diffrenames
61
        /// ).
62
        pub(crate) diff_copies: bool,
63
        /// The Git editor, from [`core.editor`](
64
        ///     https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeditor
65
        /// ).
66
        pub(crate) editor: String,
67
}
68

69
impl GitConfig {
70
        pub(super) fn new_with_config(git_config: &Config) -> Result<Self, ConfigError> {
1✔
71
                let comment_char = match get_optional_string(git_config, "core.commentChar") {
2✔
72
                        Ok(None) => "#".to_owned(),
2✔
73
                        Ok(Some(s)) if s == "auto" => "#".to_owned(),
3✔
74
                        Ok(Some(s)) => s,
1✔
75
                        Err(e) => return Err(e),
1✔
76
                };
77

78
                let (diff_renames, diff_copies) = git_diff_renames(git_config, "diff.renames")?;
2✔
79

80
                Ok(Self {
1✔
81
                        comment_char,
1✔
82
                        diff_context: get_unsigned_integer(git_config, "diff.context", 3)?,
2✔
83
                        diff_interhunk_lines: get_unsigned_integer(git_config, "diff.interHunkContext", 0)?,
2✔
84
                        diff_rename_limit: get_unsigned_integer(git_config, "diff.renameLimit", 200)?,
2✔
85
                        diff_renames,
86
                        diff_copies,
87
                        editor: get_default_editor_with_config(git_config)?,
2✔
88
                })
89
        }
90
}
91

92
impl TryFrom<&Config> for GitConfig {
93
        type Error = ConfigError;
94

95
        fn try_from(config: &Config) -> Result<Self, Self::Error> {
2✔
96
                Self::new_with_config(config)
2✔
97
        }
98
}
99

100
impl Default for GitConfig {
101
        fn default() -> Self {
1✔
102
                Self {
103
                        comment_char: "#".to_owned(),
1✔
104
                        diff_context: 3,
105
                        diff_interhunk_lines: 0,
106
                        diff_rename_limit: 200,
107
                        diff_renames: true,
108
                        diff_copies: false,
109
                        editor: get_default_editor(),
1✔
110
                }
111
        }
112
}
113

114
#[cfg(test)]
115
mod tests {
116
        use claims::{assert_err_eq, assert_ok};
117
        use rstest::rstest;
118

119
        use super::*;
120
        use crate::{
121
                config::ConfigErrorCause,
122
                test_helpers::{EnvVarAction, invalid_utf, with_env_var, with_git_config},
123
        };
124

125
        macro_rules! config_test {
126
                (
127
                        $key:ident,
128
                        $config_parent:literal,
129
                        $config_name:literal,
130
                        default $default:literal,
131
                        $($value: literal => $expected: literal),*
132
                ) => {
133
                        let config = GitConfig::default();
134
                        let value = config.$key;
135
                        assert_eq!(
136
                                value,
137
                                $default,
138
                                "Default value for '{}' was expected to be '{}' but '{}' was found",
139
                                stringify!($key),
140
                                $default,
141
                                value
142
                        );
143

144
                        for (value, expected) in [$( ($value, $expected), )*] {
145
                                let config_parent = format!("[{}]", $config_parent);
146
                                let config_value = format!("{} = \"{value}\"", $config_name);
147
                                with_git_config(&[config_parent.as_str(), config_value.as_str()], |git_config| {
148
                                        let config = GitConfig::new_with_config(&git_config).unwrap();
149
                                        assert_eq!(
150
                                                config.$key,
151
                                                expected,
152
                                                "Value for '{}' was expected to be '{}' but '{}' was found",
153
                                                stringify!($key),
154
                                                $default,
155
                                                value
156
                                        );
157
                                });
158
                        }
159
                };
160
        }
161

162
        #[test]
163
        fn try_from_git_config() {
164
                with_git_config(&[], |git_config| {
165
                        assert_ok!(GitConfig::try_from(&git_config));
166
                });
167
        }
168

169
        #[test]
170
        fn try_from_git_config_error() {
171
                with_git_config(&["[diff]", "renames = invalid"], |git_config| {
172
                        _ = GitConfig::try_from(&git_config).unwrap_err();
173
                });
174
        }
175

176
        #[rstest]
177
        fn config_values() {
178
                config_test!(comment_char, "core", "commentChar", default "#", ";" => ";", "auto" => "#");
179
                config_test!(diff_context, "diff", "context", default 3, "5" => 5);
180
                config_test!(diff_interhunk_lines, "diff", "interHunkContext", default 0, "5" => 5);
181
                config_test!(diff_interhunk_lines, "diff", "interHunkContext", default 0, "5" => 5);
182
                config_test!(diff_rename_limit, "diff", "renameLimit", default 200, "5" => 5);
183
                config_test!(diff_renames, "diff", "renames", default true, "true" => true, "false" => false, "copy" => true);
184
                config_test!(diff_copies, "diff", "renames",default false, "true" => false, "false" => false, "copy" => true);
185
        }
186

187
        #[test]
188
        fn git_editor_default_no_env() {
189
                with_env_var(
190
                        &[
191
                                EnvVarAction::Remove("GIT_EDITOR"),
192
                                EnvVarAction::Remove("VISUAL"),
193
                                EnvVarAction::Remove("EDITOR"),
194
                        ],
195
                        || {
196
                                let config = GitConfig::default();
197
                                assert_eq!(config.editor, "vi");
198
                        },
199
                );
200
        }
201

202
        #[test]
203
        fn git_editor_default_git_editor_env() {
204
                with_env_var(
205
                        &[
206
                                EnvVarAction::Remove("VISUAL"),
207
                                EnvVarAction::Remove("EDITOR"),
208
                                EnvVarAction::Set("GIT_EDITOR", String::from("git-editor")),
209
                        ],
210
                        || {
211
                                let config = GitConfig::default();
212
                                assert_eq!(config.editor, "git-editor");
213
                        },
214
                );
215
        }
216

217
        #[test]
218
        fn git_editor_default_visual_env() {
219
                with_env_var(
220
                        &[
221
                                EnvVarAction::Remove("GIT_EDITOR"),
222
                                EnvVarAction::Remove("EDITOR"),
223
                                EnvVarAction::Set("VISUAL", String::from("visual-editor")),
224
                        ],
225
                        || {
226
                                let config = GitConfig::default();
227
                                assert_eq!(config.editor, "visual-editor");
228
                        },
229
                );
230
        }
231

232
        #[test]
233
        fn git_editor_default_editor_env() {
234
                with_env_var(
235
                        &[
236
                                EnvVarAction::Remove("GIT_EDITOR"),
237
                                EnvVarAction::Remove("VISUAL"),
238
                                EnvVarAction::Set("EDITOR", String::from("editor")),
239
                        ],
240
                        || {
241
                                let config = GitConfig::default();
242
                                assert_eq!(config.editor, "editor");
243
                        },
244
                );
245
        }
246

247
        #[test]
248
        fn git_editor() {
249
                with_env_var(
250
                        &[
251
                                EnvVarAction::Remove("GIT_EDITOR"),
252
                                EnvVarAction::Remove("VISUAL"),
253
                                EnvVarAction::Remove("EDITOR"),
254
                        ],
255
                        || {
256
                                with_git_config(&["[core]", "editor = custom"], |git_config| {
257
                                        let config = GitConfig::new_with_config(&git_config).unwrap();
258
                                        assert_eq!(config.editor, "custom");
259
                                });
260
                        },
261
                );
262
        }
263

264
        #[test]
265
        fn diff_rename_limit_invalid() {
266
                with_git_config(&["[diff]", "renameLimit = invalid"], |git_config| {
267
                        assert_err_eq!(
268
                                GitConfig::new_with_config(&git_config),
269
                                ConfigError::new("diff.renameLimit", "invalid", ConfigErrorCause::InvalidUnsignedInteger),
270
                        );
271
                });
272
        }
273

274
        #[test]
275
        fn diff_rename_limit_invalid_range() {
276
                with_git_config(&["[diff]", "renameLimit = -100"], |git_config| {
277
                        assert_err_eq!(
278
                                GitConfig::new_with_config(&git_config),
279
                                ConfigError::new("diff.renameLimit", "-100", ConfigErrorCause::InvalidUnsignedInteger),
280
                        );
281
                });
282
        }
283

284
        #[test]
285
        fn diff_renames_invalid() {
286
                with_git_config(&["[diff]", "renames = invalid"], |git_config| {
287
                        assert_err_eq!(
288
                                GitConfig::new_with_config(&git_config),
289
                                ConfigError::new("diff.renames", "invalid", ConfigErrorCause::InvalidDiffRenames),
290
                        );
291
                });
292
        }
293

294
        #[test]
295
        fn git_editor_invalid() {
296
                with_env_var(
297
                        &[
298
                                EnvVarAction::Remove("GIT_EDITOR"),
299
                                EnvVarAction::Remove("VISUAL"),
300
                                EnvVarAction::Remove("EDITOR"),
301
                        ],
302
                        || {
303
                                with_git_config(
304
                                        &["[core]", format!("editor = {}", invalid_utf()).as_str()],
305
                                        |git_config| {
306
                                                assert_err_eq!(
307
                                                        GitConfig::new_with_config(&git_config),
308
                                                        ConfigError::new_read_error("core.editor", ConfigErrorCause::InvalidUtf),
309
                                                );
310
                                        },
311
                                );
312
                        },
313
                );
314
        }
315

316
        #[test]
317
        fn comment_char_invalid() {
318
                with_git_config(
319
                        &["[core]", format!("commentChar = {}", invalid_utf()).as_str()],
320
                        |git_config| {
321
                                assert_err_eq!(
322
                                        GitConfig::new_with_config(&git_config),
323
                                        ConfigError::new_read_error("core.commentChar", ConfigErrorCause::InvalidUtf),
324
                                );
325
                        },
326
                );
327
        }
328

329
        #[test]
330
        fn diff_context_invalid() {
331
                with_git_config(&["[diff]", "context = invalid"], |git_config| {
332
                        assert_err_eq!(
333
                                GitConfig::new_with_config(&git_config),
334
                                ConfigError::new("diff.context", "invalid", ConfigErrorCause::InvalidUnsignedInteger),
335
                        );
336
                });
337
        }
338

339
        #[test]
340
        fn diff_context_invalid_range() {
341
                with_git_config(&["[diff]", "context = -100"], |git_config| {
342
                        assert_err_eq!(
343
                                GitConfig::new_with_config(&git_config),
344
                                ConfigError::new("diff.context", "-100", ConfigErrorCause::InvalidUnsignedInteger),
345
                        );
346
                });
347
        }
348

349
        #[test]
350
        fn diff_interhunk_lines_invalid() {
351
                with_git_config(&["[diff]", "interHunkContext = invalid"], |git_config| {
352
                        assert_err_eq!(
353
                                GitConfig::new_with_config(&git_config),
354
                                ConfigError::new(
355
                                        "diff.interHunkContext",
356
                                        "invalid",
357
                                        ConfigErrorCause::InvalidUnsignedInteger
358
                                ),
359
                        );
360
                });
361
        }
362

363
        #[test]
364
        fn diff_interhunk_lines_invalid_range() {
365
                with_git_config(&["[diff]", "interHunkContext = -100"], |git_config| {
366
                        assert_err_eq!(
367
                                GitConfig::new_with_config(&git_config),
368
                                ConfigError::new(
369
                                        "diff.interHunkContext",
370
                                        "-100",
371
                                        ConfigErrorCause::InvalidUnsignedInteger
372
                                ),
373
                        );
374
                });
375
        }
376
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc