gix/remote/url/
rewrite.rs1use gix_features::threading::OwnShared;
2
3use crate::{
4 bstr::{BStr, BString, ByteVec},
5 config,
6 remote::Direction,
7};
8
9#[derive(Debug, Clone)]
10struct Replace {
11 find: BString,
12 with: OwnShared<BString>,
13}
14
15#[derive(Default, Debug, Clone)]
16pub(crate) struct Rewrite {
17 url_rewrite: Vec<Replace>,
18 push_url_rewrite: Vec<Replace>,
19}
20
21impl Rewrite {
23 pub fn from_config(
24 config: &gix_config::File<'static>,
25 mut filter: fn(&gix_config::file::Metadata) -> bool,
26 ) -> Rewrite {
27 config
28 .sections_by_name_and_filter("url", &mut filter)
29 .map(|sections| {
30 let mut url_rewrite = Vec::new();
31 let mut push_url_rewrite = Vec::new();
32 for section in sections {
33 let replace = match section.header().subsection_name() {
34 Some(base) => OwnShared::new(base.to_owned()),
35 None => continue,
36 };
37
38 for instead_of in section.values(config::tree::Url::INSTEAD_OF.name) {
39 url_rewrite.push(Replace {
40 with: OwnShared::clone(&replace),
41 find: instead_of.into_owned(),
42 });
43 }
44 for instead_of in section.values(config::tree::Url::PUSH_INSTEAD_OF.name) {
45 push_url_rewrite.push(Replace {
46 with: OwnShared::clone(&replace),
47 find: instead_of.into_owned(),
48 });
49 }
50 }
51 Rewrite {
52 url_rewrite,
53 push_url_rewrite,
54 }
55 })
56 .unwrap_or_default()
57 }
58}
59
60impl Rewrite {
62 fn replacements_for(&self, direction: Direction) -> &[Replace] {
63 match direction {
64 Direction::Fetch => &self.url_rewrite,
65 Direction::Push => &self.push_url_rewrite,
66 }
67 }
68
69 pub fn longest(&self, url: &gix_url::Url, direction: Direction) -> Option<BString> {
70 if self.replacements_for(direction).is_empty() {
71 None
72 } else {
73 let mut url = url.to_bstring();
74 self.rewrite_url_in_place(&mut url, direction).then_some(url)
75 }
76 }
77
78 pub fn rewrite_url_in_place(&self, url: &mut BString, direction: Direction) -> bool {
82 self.replacements_for(direction)
83 .iter()
84 .fold(None::<(usize, &BStr)>, |mut acc, replace| {
85 if url.starts_with(replace.find.as_ref()) {
86 let (bytes_matched, prev_rewrite_with) =
87 acc.get_or_insert((replace.find.len(), replace.with.as_slice().into()));
88 if *bytes_matched < replace.find.len() {
89 *bytes_matched = replace.find.len();
90 *prev_rewrite_with = replace.with.as_slice().into();
91 }
92 }
93 acc
94 })
95 .map(|(bytes_matched, replace_with)| {
96 url.replace_range(..bytes_matched, replace_with);
97 })
98 .is_some()
99 }
100}