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
use std::{borrow::Cow, collections::HashMap, convert::TryFrom, path::Path};
use super::{GitConfig, SectionId};
use crate::{
file::LookupTreeNode,
parser::{Key, ParserOrIoError, SectionHeaderName},
};
enum ResolvedTreeNode<'event> {
Terminal(HashMap<Key<'event>, Cow<'event, [u8]>>),
NonTerminal(HashMap<Cow<'event, str>, HashMap<Key<'event>, Cow<'event, [u8]>>>),
}
#[allow(clippy::module_name_repetitions)]
pub struct ResolvedGitConfig<'data>(HashMap<SectionLookupTuple<'data>, HashMap<Key<'data>, Cow<'data, [u8]>>>);
type SectionLookupTuple<'data> = (SectionHeaderName<'data>, Option<Cow<'data, str>>);
impl ResolvedGitConfig<'static> {
#[inline]
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, ParserOrIoError<'static>> {
GitConfig::open(path.as_ref()).map(Self::from)
}
}
impl<'data> ResolvedGitConfig<'data> {
#[must_use]
pub fn from_config(config: GitConfig<'data>) -> Self {
let sections: HashMap<_, _> = config
.sections
.into_iter()
.map(|(key, section_body)| {
let mut mapping: HashMap<Key, Cow<[u8]>> = HashMap::new();
for (key, value) in section_body {
mapping.insert(key, value);
}
(key, mapping)
})
.collect();
let section_name_to_node = config.section_lookup_tree.into_iter().map(|(section_name, vec)| {
let node = vec.into_iter().map(|node| match node {
LookupTreeNode::Terminal(items) => ResolvedTreeNode::Terminal(resolve_sections(§ions, items)),
LookupTreeNode::NonTerminal(mapping) => {
let items = mapping
.into_iter()
.map(|(key, items)| (key, resolve_sections(§ions, items)))
.collect();
ResolvedTreeNode::NonTerminal(items)
}
});
(section_name, node)
});
let mut resolved: HashMap<_, HashMap<Key, Cow<[u8]>>> = HashMap::new();
for (section_name, node) in section_name_to_node {
for node in node {
match node {
ResolvedTreeNode::Terminal(mapping) => {
let entry = resolved.entry((section_name.clone(), None)).or_default();
entry.extend(mapping);
}
ResolvedTreeNode::NonTerminal(mapping) => {
for (subsection, mapping) in mapping {
let entry = resolved.entry((section_name.clone(), Some(subsection))).or_default();
entry.extend(mapping);
}
}
};
}
}
Self(resolved)
}
}
fn resolve_sections<'key, 'data>(
mapping: &HashMap<SectionId, HashMap<Key<'key>, Cow<'data, [u8]>>>,
sections: Vec<SectionId>,
) -> HashMap<Key<'key>, Cow<'data, [u8]>> {
sections
.into_iter()
.flat_map(|section_id| mapping.get(§ion_id).expect("GitConfig invariant failed").iter())
.map(|(key, value)| (Key::clone(key), Cow::clone(value)))
.collect()
}
impl TryFrom<&Path> for ResolvedGitConfig<'static> {
type Error = ParserOrIoError<'static>;
#[inline]
fn try_from(path: &Path) -> Result<Self, Self::Error> {
Self::open(path)
}
}
impl<'data> From<GitConfig<'data>> for ResolvedGitConfig<'data> {
#[inline]
fn from(config: GitConfig<'data>) -> Self {
Self::from_config(config)
}
}