gix/config/tree/sections/
core.rs

1use crate::{
2    config,
3    config::tree::{keys, Core, Key, Section},
4};
5
6impl Core {
7    /// The `core.abbrev` key.
8    pub const ABBREV: Abbrev = Abbrev::new_with_validate("abbrev", &config::Tree::CORE, validate::Abbrev);
9    /// The `core.bare` key.
10    pub const BARE: keys::Boolean = keys::Boolean::new_boolean("bare", &config::Tree::CORE);
11    /// The `core.bigFileThreshold` key.
12    pub const BIG_FILE_THRESHOLD: keys::UnsignedInteger =
13        keys::UnsignedInteger::new_unsigned_integer("bigFileThreshold", &config::Tree::CORE);
14    /// The `core.checkStat` key.
15    pub const CHECK_STAT: CheckStat =
16        CheckStat::new_with_validate("checkStat", &config::Tree::CORE, validate::CheckStat);
17    /// The `core.deltaBaseCacheLimit` key.
18    pub const DELTA_BASE_CACHE_LIMIT: keys::UnsignedInteger =
19        keys::UnsignedInteger::new_unsigned_integer("deltaBaseCacheLimit", &config::Tree::CORE)
20            .with_environment_override("GIX_PACK_CACHE_MEMORY")
21            .with_note("if unset, we default to a small 64 slot fixed-size cache that holds at most 64 full delta base objects of any size. Set to 0 to deactivate it entirely");
22    /// The `core.disambiguate` key.
23    pub const DISAMBIGUATE: Disambiguate =
24        Disambiguate::new_with_validate("disambiguate", &config::Tree::CORE, validate::Disambiguate);
25    /// The `core.editor` key.
26    pub const EDITOR: keys::Program = keys::Program::new_program("editor", &config::Tree::CORE);
27    /// The `core.fileMode` key.
28    pub const FILE_MODE: keys::Boolean = keys::Boolean::new_boolean("fileMode", &config::Tree::CORE);
29    /// The `core.ignoreCase` key.
30    pub const IGNORE_CASE: keys::Boolean = keys::Boolean::new_boolean("ignoreCase", &config::Tree::CORE);
31    /// The `core.filesRefLockTimeout` key.
32    pub const FILES_REF_LOCK_TIMEOUT: keys::LockTimeout =
33        keys::LockTimeout::new_lock_timeout("filesRefLockTimeout", &config::Tree::CORE);
34    /// The `core.packedRefsTimeout` key.
35    pub const PACKED_REFS_TIMEOUT: keys::LockTimeout =
36        keys::LockTimeout::new_lock_timeout("packedRefsTimeout", &config::Tree::CORE);
37    /// The `core.multiPackIndex` key.
38    pub const MULTIPACK_INDEX: keys::Boolean = keys::Boolean::new_boolean("multiPackIndex", &config::Tree::CORE);
39    /// The `core.logAllRefUpdates` key.
40    pub const LOG_ALL_REF_UPDATES: LogAllRefUpdates =
41        LogAllRefUpdates::new_with_validate("logAllRefUpdates", &config::Tree::CORE, validate::LogAllRefUpdates);
42    /// The `core.precomposeUnicode` key.
43    ///
44    /// Needs application to use [`env::args_os`][crate::env::args_os()] to conform all input paths before they are used.
45    pub const PRECOMPOSE_UNICODE: keys::Boolean = keys::Boolean::new_boolean("precomposeUnicode", &config::Tree::CORE)
46        .with_note("application needs to conform all program input by using gix::env::args_os()");
47    /// The `core.protectHFS` key.
48    pub const PROTECT_HFS: keys::Boolean = keys::Boolean::new_boolean("protectHFS", &config::Tree::CORE);
49    /// The `core.protectNTFS` key.
50    pub const PROTECT_NTFS: keys::Boolean = keys::Boolean::new_boolean("protectNTFS", &config::Tree::CORE);
51    /// The `core.repositoryFormatVersion` key.
52    pub const REPOSITORY_FORMAT_VERSION: keys::UnsignedInteger =
53        keys::UnsignedInteger::new_unsigned_integer("repositoryFormatVersion", &config::Tree::CORE);
54    /// The `core.symlinks` key.
55    pub const SYMLINKS: keys::Boolean = keys::Boolean::new_boolean("symlinks", &config::Tree::CORE);
56    /// The `core.trustCTime` key.
57    pub const TRUST_C_TIME: keys::Boolean = keys::Boolean::new_boolean("trustCTime", &config::Tree::CORE);
58    /// The `core.worktree` key.
59    pub const WORKTREE: keys::Any = keys::Any::new("worktree", &config::Tree::CORE)
60        .with_environment_override("GIT_WORK_TREE")
61        .with_deviation("Command-line overrides also work, and they act lie an environment override. If set in the git configuration file, relative paths are relative to it.");
62    /// The `core.askPass` key.
63    pub const ASKPASS: keys::Executable = keys::Executable::new_executable("askPass", &config::Tree::CORE)
64        .with_environment_override("GIT_ASKPASS")
65        .with_note("fallback is 'SSH_ASKPASS'");
66    /// The `core.excludesFile` key.
67    pub const EXCLUDES_FILE: keys::Path = keys::Path::new_path("excludesFile", &config::Tree::CORE);
68    /// The `core.attributesFile` key.
69    pub const ATTRIBUTES_FILE: keys::Path =
70        keys::Path::new_path("attributesFile", &config::Tree::CORE)
71            .with_deviation("for checkout - it's already queried but needs building of attributes group, and of course support during checkout");
72    /// The `core.sshCommand` key.
73    pub const SSH_COMMAND: keys::Executable = keys::Executable::new_executable("sshCommand", &config::Tree::CORE)
74        .with_environment_override("GIT_SSH_COMMAND");
75    /// The `core.useReplaceRefs` key.
76    pub const USE_REPLACE_REFS: keys::Boolean = keys::Boolean::new_boolean("useReplaceRefs", &config::Tree::CORE)
77        .with_environment_override("GIT_NO_REPLACE_OBJECTS");
78    /// The `core.commitGraph` key.
79    pub const COMMIT_GRAPH: keys::Boolean = keys::Boolean::new_boolean("commitGraph", &config::Tree::CORE);
80    /// The `core.safecrlf` key.
81    #[cfg(feature = "attributes")]
82    pub const SAFE_CRLF: SafeCrlf = SafeCrlf::new_with_validate("safecrlf", &config::Tree::CORE, validate::SafeCrlf);
83    /// The `core.autocrlf` key.
84    #[cfg(feature = "attributes")]
85    pub const AUTO_CRLF: AutoCrlf = AutoCrlf::new_with_validate("autocrlf", &config::Tree::CORE, validate::AutoCrlf);
86    /// The `core.eol` key.
87    #[cfg(feature = "attributes")]
88    pub const EOL: Eol = Eol::new_with_validate("eol", &config::Tree::CORE, validate::Eol);
89    /// The `core.checkRoundTripEncoding` key.
90    #[cfg(feature = "attributes")]
91    pub const CHECK_ROUND_TRIP_ENCODING: CheckRoundTripEncoding = CheckRoundTripEncoding::new_with_validate(
92        "checkRoundTripEncoding",
93        &config::Tree::CORE,
94        validate::CheckRoundTripEncoding,
95    );
96}
97
98impl Section for Core {
99    fn name(&self) -> &str {
100        "core"
101    }
102
103    fn keys(&self) -> &[&dyn Key] {
104        &[
105            &Self::ABBREV,
106            &Self::BARE,
107            &Self::BIG_FILE_THRESHOLD,
108            &Self::CHECK_STAT,
109            &Self::DELTA_BASE_CACHE_LIMIT,
110            &Self::DISAMBIGUATE,
111            &Self::EDITOR,
112            &Self::FILE_MODE,
113            &Self::IGNORE_CASE,
114            &Self::FILES_REF_LOCK_TIMEOUT,
115            &Self::PACKED_REFS_TIMEOUT,
116            &Self::MULTIPACK_INDEX,
117            &Self::LOG_ALL_REF_UPDATES,
118            &Self::PRECOMPOSE_UNICODE,
119            &Self::REPOSITORY_FORMAT_VERSION,
120            &Self::SYMLINKS,
121            &Self::TRUST_C_TIME,
122            &Self::WORKTREE,
123            &Self::PROTECT_HFS,
124            &Self::PROTECT_NTFS,
125            &Self::ASKPASS,
126            &Self::EXCLUDES_FILE,
127            &Self::ATTRIBUTES_FILE,
128            &Self::SSH_COMMAND,
129            &Self::USE_REPLACE_REFS,
130            &Self::COMMIT_GRAPH,
131            #[cfg(feature = "attributes")]
132            &Self::SAFE_CRLF,
133            #[cfg(feature = "attributes")]
134            &Self::AUTO_CRLF,
135            #[cfg(feature = "attributes")]
136            &Self::EOL,
137            #[cfg(feature = "attributes")]
138            &Self::CHECK_ROUND_TRIP_ENCODING,
139        ]
140    }
141}
142
143/// The `core.checkStat` key.
144pub type CheckStat = keys::Any<validate::CheckStat>;
145
146/// The `core.abbrev` key.
147pub type Abbrev = keys::Any<validate::Abbrev>;
148
149/// The `core.logAllRefUpdates` key.
150pub type LogAllRefUpdates = keys::Any<validate::LogAllRefUpdates>;
151
152/// The `core.disambiguate` key.
153pub type Disambiguate = keys::Any<validate::Disambiguate>;
154
155#[cfg(feature = "attributes")]
156mod filter {
157    use super::validate;
158    use crate::config::tree::keys;
159
160    /// The `core.safecrlf` key.
161    pub type SafeCrlf = keys::Any<validate::SafeCrlf>;
162
163    /// The `core.autocrlf` key.
164    pub type AutoCrlf = keys::Any<validate::AutoCrlf>;
165
166    /// The `core.eol` key.
167    pub type Eol = keys::Any<validate::Eol>;
168
169    /// The `core.checkRoundTripEncoding` key.
170    pub type CheckRoundTripEncoding = keys::Any<validate::CheckRoundTripEncoding>;
171
172    mod check_round_trip_encoding {
173        use std::borrow::Cow;
174
175        use crate::{
176            bstr::{BStr, ByteSlice},
177            config,
178            config::tree::{core::CheckRoundTripEncoding, Key},
179        };
180
181        impl CheckRoundTripEncoding {
182            /// Convert `value` into a list of encodings, which are either space or coma separated. Fail if an encoding is unknown.
183            /// If `None`, the default is returned.
184            pub fn try_into_encodings(
185                &'static self,
186                value: Option<Cow<'_, BStr>>,
187            ) -> Result<Vec<&'static gix_filter::encoding::Encoding>, config::encoding::Error> {
188                Ok(match value {
189                    None => vec![gix_filter::encoding::SHIFT_JIS],
190                    Some(value) => {
191                        let mut out = Vec::new();
192                        for encoding in value
193                            .as_ref()
194                            .split(|b| *b == b',' || *b == b' ')
195                            .filter(|e| !e.trim().is_empty())
196                        {
197                            out.push(
198                                gix_filter::encoding::Encoding::for_label(encoding.trim()).ok_or_else(|| {
199                                    config::encoding::Error {
200                                        key: self.logical_name().into(),
201                                        value: value.as_ref().to_owned(),
202                                        encoding: encoding.into(),
203                                    }
204                                })?,
205                            );
206                        }
207                        out
208                    }
209                })
210            }
211        }
212    }
213
214    mod eol {
215        use std::borrow::Cow;
216
217        use crate::{
218            bstr::{BStr, ByteSlice},
219            config,
220            config::tree::core::Eol,
221        };
222
223        impl Eol {
224            /// Convert `value` into the default end-of-line mode.
225            ///
226            /// ### Deviation
227            ///
228            /// git will allow any value and silently leaves it unset, we will fail if the value is not known.
229            pub fn try_into_eol(
230                &'static self,
231                value: Cow<'_, BStr>,
232            ) -> Result<gix_filter::eol::Mode, config::key::GenericErrorWithValue> {
233                Ok(match value.to_str_lossy().as_ref() {
234                    "lf" => gix_filter::eol::Mode::Lf,
235                    "crlf" => gix_filter::eol::Mode::CrLf,
236                    "native" => gix_filter::eol::Mode::default(),
237                    _ => return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned())),
238                })
239            }
240        }
241    }
242
243    mod safecrlf {
244        use std::borrow::Cow;
245
246        use gix_filter::pipeline::CrlfRoundTripCheck;
247
248        use crate::{bstr::BStr, config, config::tree::core::SafeCrlf};
249
250        impl SafeCrlf {
251            /// Convert `value` into the safe-crlf enumeration, if possible.
252            pub fn try_into_safecrlf(
253                &'static self,
254                value: Cow<'_, BStr>,
255            ) -> Result<CrlfRoundTripCheck, config::key::GenericErrorWithValue> {
256                if value.as_ref() == "warn" {
257                    return Ok(CrlfRoundTripCheck::Warn);
258                }
259                let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| {
260                    config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err)
261                })?;
262                Ok(if value.into() {
263                    CrlfRoundTripCheck::Fail
264                } else {
265                    CrlfRoundTripCheck::Skip
266                })
267            }
268        }
269    }
270
271    mod autocrlf {
272        use std::borrow::Cow;
273
274        use gix_filter::eol;
275
276        use crate::{bstr::BStr, config, config::tree::core::AutoCrlf};
277
278        impl AutoCrlf {
279            /// Convert `value` into the safe-crlf enumeration, if possible.
280            pub fn try_into_autocrlf(
281                &'static self,
282                value: Cow<'_, BStr>,
283            ) -> Result<eol::AutoCrlf, config::key::GenericErrorWithValue> {
284                if value.as_ref() == "input" {
285                    return Ok(eol::AutoCrlf::Input);
286                }
287                let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| {
288                    config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err)
289                })?;
290                Ok(if value.into() {
291                    eol::AutoCrlf::Enabled
292                } else {
293                    eol::AutoCrlf::Disabled
294                })
295            }
296        }
297    }
298}
299#[cfg(feature = "attributes")]
300pub use filter::*;
301
302#[cfg(feature = "revision")]
303mod disambiguate {
304    use std::borrow::Cow;
305
306    use crate::{
307        bstr::{BStr, ByteSlice},
308        config,
309        config::tree::core::Disambiguate,
310        revision::spec::parse::ObjectKindHint,
311    };
312
313    impl Disambiguate {
314        /// Convert a disambiguation marker into the respective enum.
315        pub fn try_into_object_kind_hint(
316            &'static self,
317            value: Cow<'_, BStr>,
318        ) -> Result<Option<ObjectKindHint>, config::key::GenericErrorWithValue> {
319            let hint = match value.as_ref().as_bytes() {
320                b"none" => return Ok(None),
321                b"commit" => ObjectKindHint::Commit,
322                b"committish" => ObjectKindHint::Committish,
323                b"tree" => ObjectKindHint::Tree,
324                b"treeish" => ObjectKindHint::Treeish,
325                b"blob" => ObjectKindHint::Blob,
326                _ => return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned())),
327            };
328            Ok(Some(hint))
329        }
330    }
331}
332
333mod log_all_ref_updates {
334    use crate::{config, config::tree::core::LogAllRefUpdates};
335
336    impl LogAllRefUpdates {
337        /// Returns the mode for ref-updates as parsed from `value`. If `value` is not a boolean, we try
338        /// to interpret the string value instead. For correctness, this two step process is necessary as
339        /// the interpretation of booleans in special in `git-config`, i.e. we can't just treat it as string.
340        pub fn try_into_ref_updates(
341            &'static self,
342            value: Option<Result<bool, gix_config::value::Error>>,
343        ) -> Result<Option<gix_ref::store::WriteReflog>, config::key::GenericErrorWithValue> {
344            match value {
345                Some(Ok(bool)) => Ok(Some(if bool {
346                    gix_ref::store::WriteReflog::Normal
347                } else {
348                    gix_ref::store::WriteReflog::Disable
349                })),
350                Some(Err(err)) => match err.input {
351                    val if val.eq_ignore_ascii_case(b"always") => Ok(Some(gix_ref::store::WriteReflog::Always)),
352                    val => Err(config::key::GenericErrorWithValue::from_value(self, val)),
353                },
354                None => Ok(None),
355            }
356        }
357    }
358}
359
360mod check_stat {
361    use std::borrow::Cow;
362
363    use crate::{
364        bstr::{BStr, ByteSlice},
365        config,
366        config::tree::core::CheckStat,
367    };
368
369    impl CheckStat {
370        /// Returns true if the full set of stat entries should be checked, and it's just as lenient as git.
371        pub fn try_into_checkstat(
372            &'static self,
373            value: Cow<'_, BStr>,
374        ) -> Result<bool, config::key::GenericErrorWithValue> {
375            Ok(match value.as_ref().as_bytes() {
376                b"minimal" => false,
377                b"default" => true,
378                _ => {
379                    return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned()));
380                }
381            })
382        }
383    }
384}
385
386mod abbrev {
387    use std::borrow::Cow;
388
389    use config::abbrev::Error;
390
391    use crate::{
392        bstr::{BStr, ByteSlice},
393        config,
394        config::tree::core::Abbrev,
395    };
396
397    impl Abbrev {
398        /// Convert the given `hex_len_str` into the amount of characters that a short hash should have.
399        /// If `None` is returned, the correct value can be determined based on the amount of objects in the repo.
400        pub fn try_into_abbreviation(
401            &'static self,
402            hex_len_str: Cow<'_, BStr>,
403            object_hash: gix_hash::Kind,
404        ) -> Result<Option<usize>, Error> {
405            let max = object_hash.len_in_hex() as u8;
406            if hex_len_str.trim().is_empty() {
407                return Err(Error {
408                    value: hex_len_str.into_owned(),
409                    max,
410                });
411            }
412            if hex_len_str.trim().eq_ignore_ascii_case(b"auto") {
413                Ok(None)
414            } else {
415                let value_bytes = hex_len_str.as_ref();
416                if let Ok(false) = gix_config::Boolean::try_from(value_bytes).map(Into::into) {
417                    Ok(object_hash.len_in_hex().into())
418                } else {
419                    let value = gix_config::Integer::try_from(value_bytes)
420                        .map_err(|_| Error {
421                            value: hex_len_str.clone().into_owned(),
422                            max,
423                        })?
424                        .to_decimal()
425                        .ok_or_else(|| Error {
426                            value: hex_len_str.clone().into_owned(),
427                            max,
428                        })?;
429                    if value < 4 || value as usize > object_hash.len_in_hex() {
430                        return Err(Error {
431                            value: hex_len_str.clone().into_owned(),
432                            max,
433                        });
434                    }
435                    Ok(Some(value as usize))
436                }
437            }
438        }
439    }
440}
441
442mod validate {
443    use crate::{bstr::BStr, config::tree::keys};
444
445    pub struct Disambiguate;
446    impl keys::Validate for Disambiguate {
447        #[cfg_attr(not(feature = "revision"), allow(unused_variables))]
448        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
449            #[cfg(feature = "revision")]
450            super::Core::DISAMBIGUATE.try_into_object_kind_hint(value.into())?;
451            Ok(())
452        }
453    }
454
455    pub struct LogAllRefUpdates;
456    impl keys::Validate for LogAllRefUpdates {
457        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
458            super::Core::LOG_ALL_REF_UPDATES
459                .try_into_ref_updates(Some(gix_config::Boolean::try_from(value).map(|b| b.0)))?;
460            Ok(())
461        }
462    }
463
464    pub struct CheckStat;
465    impl keys::Validate for CheckStat {
466        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
467            super::Core::CHECK_STAT.try_into_checkstat(value.into())?;
468            Ok(())
469        }
470    }
471
472    pub struct Abbrev;
473    impl keys::Validate for Abbrev {
474        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
475            // TODO: when there is options, validate against all hashes and assure all fail to trigger a validation failure.
476            super::Core::ABBREV.try_into_abbreviation(value.into(), gix_hash::Kind::Sha1)?;
477            Ok(())
478        }
479    }
480
481    pub struct SafeCrlf;
482    impl keys::Validate for SafeCrlf {
483        #[cfg_attr(not(feature = "attributes"), allow(unused_variables))]
484        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
485            #[cfg(feature = "attributes")]
486            super::Core::SAFE_CRLF.try_into_safecrlf(value.into())?;
487            Ok(())
488        }
489    }
490
491    pub struct AutoCrlf;
492    impl keys::Validate for AutoCrlf {
493        #[cfg_attr(not(feature = "attributes"), allow(unused_variables))]
494        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
495            #[cfg(feature = "attributes")]
496            super::Core::AUTO_CRLF.try_into_autocrlf(value.into())?;
497            Ok(())
498        }
499    }
500
501    pub struct Eol;
502    impl keys::Validate for Eol {
503        #[cfg_attr(not(feature = "attributes"), allow(unused_variables))]
504        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
505            #[cfg(feature = "attributes")]
506            super::Core::EOL.try_into_eol(value.into())?;
507            Ok(())
508        }
509    }
510
511    pub struct CheckRoundTripEncoding;
512    impl keys::Validate for CheckRoundTripEncoding {
513        #[cfg_attr(not(feature = "attributes"), allow(unused_variables))]
514        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
515            #[cfg(feature = "attributes")]
516            super::Core::CHECK_ROUND_TRIP_ENCODING.try_into_encodings(Some(value.into()))?;
517            Ok(())
518        }
519    }
520}