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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use crate::{
    bstr::{BStr, BString, ByteVec},
    config::tree::key::validate_assignment,
};

/// Provide information about a configuration section.
pub trait Section {
    /// The section name, like `remote` in `remote.origin.url`.
    fn name(&self) -> &str;
    /// The keys directly underneath it for carrying configuration values.
    fn keys(&self) -> &[&dyn Key];
    /// The list of sub-section names, which may be empty if there are no statically known sub-sections.
    fn sub_sections(&self) -> &[&dyn Section] {
        &[]
    }
    /// The parent section if this is a statically known sub-section.
    fn parent(&self) -> Option<&dyn Section> {
        None
    }
}

/// Determine how subsections may be used with a given key, suitable for obtaining the full name for use in assignments.
#[derive(Debug, Copy, Clone)]
pub enum SubSectionRequirement {
    /// Subsections must not be used, this key can only be below a section.
    Never,
    /// The sub-section is used as parameter with the given name.
    Parameter(&'static str),
}

/// A way to link a key with other resources.
#[derive(Debug, Copy, Clone)]
pub enum Link {
    /// The environment variable of the given name will override the value of this key.
    EnvironmentOverride(&'static str),
    /// This config key is used as fallback if this key isn't set.
    FallbackKey(&'static dyn Key),
}

/// A note attached to a key.
#[derive(Debug, Copy, Clone)]
pub enum Note {
    /// A piece of information related to a key to help the user.
    Informative(&'static str),
    /// This key works differently than is described by git, explaining the deviation further.
    Deviation(&'static str),
}

/// A leaf-level entry in the git configuration, like `url` in `remote.origin.url`.
pub trait Key: std::fmt::Debug {
    /// The key's name, like `url` in `remote.origin.url`.
    fn name(&self) -> &str;
    /// See if `value` is allowed as value of this key, or return a descriptive error if it is not.
    fn validate(&self, value: &BStr) -> Result<(), crate::config::tree::key::validate::Error>;
    /// The section containing this key. Git configuration has no free-standing keys, they are always underneath a section.
    fn section(&self) -> &dyn Section;
    /// The return value encodes three possible states to indicate subsection requirements
    /// * `None` = subsections may or may not be used, the most flexible setting.
    /// * `Some([Requirement][SubSectionRequirement])` = subsections must or must not be used, depending on the value
    fn subsection_requirement(&self) -> Option<&SubSectionRequirement> {
        Some(&SubSectionRequirement::Never)
    }
    /// Return the link to other resources, if available.
    fn link(&self) -> Option<&Link> {
        None
    }
    /// Return a note about this key, if available.
    fn note(&self) -> Option<&Note> {
        None
    }

    /// Return the name of an environment variable that would override this value (after following links until one is found).
    fn environment_override(&self) -> Option<&str> {
        let mut cursor = self.link()?;
        loop {
            match cursor {
                Link::EnvironmentOverride(name) => return Some(name),
                Link::FallbackKey(next) => {
                    cursor = next.link()?;
                }
            }
        }
    }

    /// Return the environment override that must be set on this key.
    /// # Panics
    /// If no environment variable is set
    fn the_environment_override(&self) -> &str {
        self.environment_override()
            .expect("BUG: environment override must be set")
    }
    /// Produce a name that describes how the name is composed. This is `core.bare` for statically known keys, or `branch.<name>.key`
    /// for complex ones.
    fn logical_name(&self) -> String {
        let section = self.section();
        let mut buf = String::new();
        let parameter = if let Some(parent) = section.parent() {
            buf.push_str(parent.name());
            buf.push('.');
            None
        } else {
            self.subsection_requirement().and_then(|requirement| match requirement {
                SubSectionRequirement::Parameter(name) => Some(name),
                SubSectionRequirement::Never => None,
            })
        };
        buf.push_str(section.name());
        buf.push('.');
        if let Some(parameter) = parameter {
            buf.push('<');
            buf.push_str(parameter);
            buf.push('>');
            buf.push('.');
        }
        buf.push_str(self.name());
        buf
    }

    /// The full name of the key for use in configuration overrides, like `core.bare`, or `remote.<subsection>.url` if `subsection` is
    /// not `None`.
    /// May fail if this key needs a subsection, or may not have a subsection.
    fn full_name(&self, subsection: Option<&BStr>) -> Result<BString, String> {
        let section = self.section();
        let mut buf = BString::default();
        let subsection = match self.subsection_requirement() {
            None => subsection,
            Some(requirement) => match (requirement, subsection) {
                (SubSectionRequirement::Never, Some(_)) => {
                    return Err(format!(
                        "The key named '{}' cannot be used with non-static subsections.",
                        self.logical_name()
                    ));
                }
                (SubSectionRequirement::Parameter(_), None) => {
                    return Err(format!(
                        "The key named '{}' cannot be used without subsections.",
                        self.logical_name()
                    ))
                }
                _ => subsection,
            },
        };

        if let Some(parent) = section.parent() {
            buf.push_str(parent.name());
            buf.push(b'.');
        }
        buf.push_str(section.name());
        buf.push(b'.');
        if let Some(subsection) = subsection {
            debug_assert!(
                section.parent().is_none(),
                "BUG: sections with parameterized sub-sections must be top-level sections"
            );
            buf.push_str(subsection);
            buf.push(b'.');
        }
        buf.push_str(self.name());
        Ok(buf)
    }

    /// Return an assignment with the keys full name to `value`, suitable for [configuration overrides][crate::open::Options::config_overrides()].
    /// Note that this will fail if the key requires a subsection name.
    fn validated_assignment(&self, value: &BStr) -> Result<BString, validate_assignment::Error> {
        self.validate(value)?;
        let mut key = self
            .full_name(None)
            .map_err(|message| validate_assignment::Error::Name { message })?;
        key.push(b'=');
        key.push_str(value);
        Ok(key)
    }

    /// Return an assignment with the keys full name to `value`, suitable for [configuration overrides][crate::open::Options::config_overrides()].
    /// Note that this will fail if the key requires a subsection name.
    fn validated_assignment_fmt(
        &self,
        value: &dyn std::fmt::Display,
    ) -> Result<BString, crate::config::tree::key::validate_assignment::Error> {
        let value = value.to_string();
        self.validated_assignment(value.as_str().into())
    }

    /// Return an assignment to `value` with the keys full name within `subsection`, suitable for [configuration overrides][crate::open::Options::config_overrides()].
    /// Note that this is only valid if this key supports parameterized sub-sections, or else an error is returned.
    fn validated_assignment_with_subsection(
        &self,
        value: &BStr,
        subsection: &BStr,
    ) -> Result<BString, crate::config::tree::key::validate_assignment::Error> {
        self.validate(value)?;
        let mut key = self
            .full_name(Some(subsection))
            .map_err(|message| validate_assignment::Error::Name { message })?;
        key.push(b'=');
        key.push_str(value);
        Ok(key)
    }
}