summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/config/tree/sections/diff.rs
blob: 103bb7001f721ed2fd7df5ef9f0c984e55db654c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use crate::{
    config,
    config::tree::{keys, Diff, Key, Section},
};

impl Diff {
    /// The `diff.algorithm` key.
    pub const ALGORITHM: Algorithm = Algorithm::new_with_validate("algorithm", &config::Tree::DIFF, validate::Algorithm)
                                        .with_deviation("'patience' diff is not implemented and can default to 'histogram' if lenient config is used, and defaults to histogram if unset for fastest and best results");
    /// The `diff.renameLimit` key.
    pub const RENAME_LIMIT: keys::UnsignedInteger = keys::UnsignedInteger::new_unsigned_integer(
        "renameLimit",
        &config::Tree::DIFF,
    )
    .with_note(
        "The limit is actually squared, so 1000 stands for up to 1 million diffs if fuzzy rename tracking is enabled",
    );
    /// The `diff.renames` key.
    pub const RENAMES: Renames = Renames::new_renames("renames", &config::Tree::DIFF);
}

impl Section for Diff {
    fn name(&self) -> &str {
        "diff"
    }

    fn keys(&self) -> &[&dyn Key] {
        &[&Self::ALGORITHM, &Self::RENAME_LIMIT, &Self::RENAMES]
    }
}

/// The `diff.algorithm` key.
pub type Algorithm = keys::Any<validate::Algorithm>;

/// The `diff.renames` key.
pub type Renames = keys::Any<validate::Renames>;

mod algorithm {
    use std::borrow::Cow;

    use crate::{
        bstr::BStr,
        config,
        config::{diff::algorithm::Error, tree::sections::diff::Algorithm},
    };

    impl Algorithm {
        /// Derive the diff algorithm identified by `name`, case-insensitively.
        pub fn try_into_algorithm(&self, name: Cow<'_, BStr>) -> Result<gix_diff::blob::Algorithm, Error> {
            let algo = if name.eq_ignore_ascii_case(b"myers") || name.eq_ignore_ascii_case(b"default") {
                gix_diff::blob::Algorithm::Myers
            } else if name.eq_ignore_ascii_case(b"minimal") {
                gix_diff::blob::Algorithm::MyersMinimal
            } else if name.eq_ignore_ascii_case(b"histogram") {
                gix_diff::blob::Algorithm::Histogram
            } else if name.eq_ignore_ascii_case(b"patience") {
                return Err(config::diff::algorithm::Error::Unimplemented {
                    name: name.into_owned(),
                });
            } else {
                return Err(Error::Unknown {
                    name: name.into_owned(),
                });
            };
            Ok(algo)
        }
    }
}

mod renames {
    use std::borrow::Cow;

    use crate::{
        bstr::{BStr, ByteSlice},
        config::{
            key::GenericError,
            tree::{keys, sections::diff::Renames, Section},
        },
        diff::rename::Tracking,
    };

    impl Renames {
        /// Create a new instance.
        pub const fn new_renames(name: &'static str, section: &'static dyn Section) -> Self {
            keys::Any::new_with_validate(name, section, super::validate::Renames)
        }
        /// Try to convert the configuration into a valid rename tracking variant. Use `value` and if it's an error, call `value_string`
        /// to try and interpret the key as string.
        pub fn try_into_renames<'a>(
            &'static self,
            value: Result<bool, gix_config::value::Error>,
            value_string: impl FnOnce() -> Option<Cow<'a, BStr>>,
        ) -> Result<Tracking, GenericError> {
            Ok(match value {
                Ok(true) => Tracking::Renames,
                Ok(false) => Tracking::Disabled,
                Err(err) => {
                    let value = value_string().ok_or_else(|| GenericError::from(self))?;
                    match value.as_ref().as_bytes() {
                        b"copy" | b"copies" => Tracking::RenamesAndCopies,
                        _ => return Err(GenericError::from_value(self, value.into_owned()).with_source(err)),
                    }
                }
            })
        }
    }
}

mod validate {
    use std::borrow::Cow;

    use crate::{
        bstr::BStr,
        config::tree::{keys, Diff},
    };

    pub struct Algorithm;
    impl keys::Validate for Algorithm {
        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
            Diff::ALGORITHM.try_into_algorithm(value.into())?;
            Ok(())
        }
    }

    pub struct Renames;
    impl keys::Validate for Renames {
        fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
            let boolean = gix_config::Boolean::try_from(value).map(|b| b.0);
            Diff::RENAMES.try_into_renames(boolean, || Some(Cow::Borrowed(value)))?;
            Ok(())
        }
    }
}