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
|
use gix_features::threading::OwnShared;
use crate::{
bstr::{BStr, BString, ByteVec},
config,
remote::Direction,
};
#[derive(Debug, Clone)]
struct Replace {
find: BString,
with: OwnShared<BString>,
}
#[derive(Default, Debug, Clone)]
pub(crate) struct Rewrite {
url_rewrite: Vec<Replace>,
push_url_rewrite: Vec<Replace>,
}
/// Init
impl Rewrite {
pub fn from_config(
config: &gix_config::File<'static>,
mut filter: fn(&gix_config::file::Metadata) -> bool,
) -> Rewrite {
config
.sections_by_name_and_filter("url", &mut filter)
.map(|sections| {
let mut url_rewrite = Vec::new();
let mut push_url_rewrite = Vec::new();
for section in sections {
let replace = match section.header().subsection_name() {
Some(base) => OwnShared::new(base.to_owned()),
None => continue,
};
for instead_of in section.values(config::tree::Url::INSTEAD_OF.name) {
url_rewrite.push(Replace {
with: OwnShared::clone(&replace),
find: instead_of.into_owned(),
});
}
for instead_of in section.values(config::tree::Url::PUSH_INSTEAD_OF.name) {
push_url_rewrite.push(Replace {
with: OwnShared::clone(&replace),
find: instead_of.into_owned(),
});
}
}
Rewrite {
url_rewrite,
push_url_rewrite,
}
})
.unwrap_or_default()
}
}
/// Access
impl Rewrite {
fn replacements_for(&self, direction: Direction) -> &[Replace] {
match direction {
Direction::Fetch => &self.url_rewrite,
Direction::Push => &self.push_url_rewrite,
}
}
pub fn longest(&self, url: &gix_url::Url, direction: Direction) -> Option<BString> {
if self.replacements_for(direction).is_empty() {
None
} else {
let mut url = url.to_bstring();
self.rewrite_url_in_place(&mut url, direction).then_some(url)
}
}
/// Rewrite the given `url` of `direction` and return `true` if a replacement happened.
///
/// Note that the result must still be checked for validity, it might not be a valid URL as we do a syntax-unaware replacement.
pub fn rewrite_url_in_place(&self, url: &mut BString, direction: Direction) -> bool {
self.replacements_for(direction)
.iter()
.fold(None::<(usize, &BStr)>, |mut acc, replace| {
if url.starts_with(replace.find.as_ref()) {
let (bytes_matched, prev_rewrite_with) =
acc.get_or_insert((replace.find.len(), replace.with.as_slice().into()));
if *bytes_matched < replace.find.len() {
*bytes_matched = replace.find.len();
*prev_rewrite_with = replace.with.as_slice().into();
}
};
acc
})
.map(|(bytes_matched, replace_with)| {
url.replace_range(..bytes_matched, replace_with);
})
.is_some()
}
}
|