use crate::bstr::{BStr, BString, ByteVec};use crate::remote::Direction;use git_features::threading::OwnShared; #[derive(Debug, Clone)]pub(crate) struct Replace { find: BString, with: OwnShared<BString>,} #[derive(Default, Debug, Clone)]pub(crate) struct Rewrite { url_rewrite: Vec<Replace>, push_url_rewrite: Vec<Replace>,} /// Initimpl Rewrite { pub fn from_config( config: &git_config::File<'static>, mut filter: fn(&git_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("insteadOf") { url_rewrite.push(Replace { with: OwnShared::clone(&replace), find: instead_of.into_owned(), }); } for instead_of in section.values("pushInsteadOf") { push_url_rewrite.push(Replace { with: OwnShared::clone(&replace), find: instead_of.into_owned(), }); } } Rewrite { url_rewrite, push_url_rewrite, } }) .unwrap_or_default() }} /// Accessimpl 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: &git_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(|| 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() }}
use crate::bstr::{BStr, BString, ByteVec};
use crate::remote::Direction;
use git_features::threading::OwnShared;
#[derive(Debug, Clone)]
pub(crate) 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: &git_config::File<'static>,
mut filter: fn(&git_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("insteadOf") {
url_rewrite.push(Replace {
with: OwnShared::clone(&replace),
find: instead_of.into_owned(),
});
}
for instead_of in section.values("pushInsteadOf") {
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: &git_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(|| 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()
}
}