]> git.proxmox.com Git - rustc.git/blame - vendor/gix/src/repository/remote.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / vendor / gix / src / repository / remote.rs
CommitLineData
0a29b90c
FG
1#![allow(clippy::result_large_err)]
2use std::convert::TryInto;
3
4use crate::{bstr::BStr, config, remote, remote::find, Remote};
5
6impl crate::Repository {
7 /// Create a new remote available at the given `url`.
8 ///
9 /// It's configured to fetch included tags by default, similar to git.
10 /// See [`with_fetch_tags(…)`][Remote::with_fetch_tags()] for a way to change it.
11 pub fn remote_at<Url, E>(&self, url: Url) -> Result<Remote<'_>, remote::init::Error>
12 where
13 Url: TryInto<gix_url::Url, Error = E>,
14 gix_url::parse::Error: From<E>,
15 {
16 Remote::from_fetch_url(url, true, self)
17 }
18
19 /// Create a new remote available at the given `url` similarly to [`remote_at()`][crate::Repository::remote_at()],
20 /// but don't rewrite the url according to rewrite rules.
21 /// This eliminates a failure mode in case the rewritten URL is faulty, allowing to selectively [apply rewrite
22 /// rules][Remote::rewrite_urls()] later and do so non-destructively.
23 pub fn remote_at_without_url_rewrite<Url, E>(&self, url: Url) -> Result<Remote<'_>, remote::init::Error>
24 where
25 Url: TryInto<gix_url::Url, Error = E>,
26 gix_url::parse::Error: From<E>,
27 {
28 Remote::from_fetch_url(url, false, self)
29 }
30
781aab86
FG
31 /// Find the configured remote with the given `name_or_url` or report an error,
32 /// similar to [`try_find_remote(…)`][Self::try_find_remote()].
0a29b90c
FG
33 ///
34 /// Note that we will obtain remotes only if we deem them [trustworthy][crate::open::Options::filter_config_section()].
35 pub fn find_remote<'a>(&self, name_or_url: impl Into<&'a BStr>) -> Result<Remote<'_>, find::existing::Error> {
36 let name_or_url = name_or_url.into();
37 Ok(self
38 .try_find_remote(name_or_url)
39 .ok_or_else(|| find::existing::Error::NotFound {
40 name: name_or_url.into(),
41 })??)
42 }
43
44 /// Find the default remote as configured, or `None` if no such configuration could be found.
45 ///
781aab86 46 /// See [`remote_default_name()`](Self::remote_default_name()) for more information on the `direction` parameter.
0a29b90c
FG
47 pub fn find_default_remote(
48 &self,
49 direction: remote::Direction,
50 ) -> Option<Result<Remote<'_>, find::existing::Error>> {
51 self.remote_default_name(direction)
52 .map(|name| self.find_remote(name.as_ref()))
53 }
54
781aab86
FG
55 /// Find the configured remote with the given `name_or_url` or return `None` if it doesn't exist,
56 /// for the purpose of fetching or pushing data.
0a29b90c
FG
57 ///
58 /// There are various error kinds related to partial information or incorrectly formatted URLs or ref-specs.
59 /// Also note that the created `Remote` may have neither fetch nor push ref-specs set at all.
60 ///
61 /// Note that ref-specs are de-duplicated right away which may change their order. This doesn't affect matching in any way
62 /// as negations/excludes are applied after includes.
63 ///
64 /// We will only include information if we deem it [trustworthy][crate::open::Options::filter_config_section()].
65 pub fn try_find_remote<'a>(&self, name_or_url: impl Into<&'a BStr>) -> Option<Result<Remote<'_>, find::Error>> {
781aab86
FG
66 self.try_find_remote_inner(name_or_url.into(), true)
67 }
68
69 /// This method emulate what `git fetch <remote>` does in order to obtain a remote to fetch from.
70 ///
71 /// As such, with `name_or_url` being `Some`, it will:
72 ///
73 /// * use `name_or_url` verbatim if it is a URL, creating a new remote in memory as needed.
74 /// * find the named remote if `name_or_url` is a remote name
75 ///
76 /// If `name_or_url` is `None`:
77 ///
78 /// * use the current `HEAD` branch to find a configured remote
79 /// * fall back to either a generally configured remote or the only configured remote.
80 ///
81 /// Fail if no remote could be found despite all of the above.
82 pub fn find_fetch_remote(&self, name_or_url: Option<&BStr>) -> Result<Remote<'_>, find::for_fetch::Error> {
83 Ok(match name_or_url {
84 Some(name) => match self.try_find_remote(name).and_then(Result::ok) {
85 Some(remote) => remote,
86 None => self.remote_at(gix_url::parse(name)?)?,
87 },
88 None => self
89 .head()?
90 .into_remote(remote::Direction::Fetch)
91 .transpose()?
92 .map(Ok)
93 .or_else(|| self.find_default_remote(remote::Direction::Fetch))
94 .ok_or_else(|| find::for_fetch::Error::ExactlyOneRemoteNotAvailable)??,
95 })
0a29b90c
FG
96 }
97
fe692bf9 98 /// Similar to [`try_find_remote()`][Self::try_find_remote()], but removes a failure mode if rewritten URLs turn out to be invalid
0a29b90c
FG
99 /// as it skips rewriting them.
100 /// Use this in conjunction with [`Remote::rewrite_urls()`] to non-destructively apply the rules and keep the failed urls unchanged.
101 pub fn try_find_remote_without_url_rewrite<'a>(
102 &self,
103 name_or_url: impl Into<&'a BStr>,
104 ) -> Option<Result<Remote<'_>, find::Error>> {
781aab86 105 self.try_find_remote_inner(name_or_url.into(), false)
0a29b90c
FG
106 }
107
108 fn try_find_remote_inner<'a>(
109 &self,
110 name_or_url: impl Into<&'a BStr>,
111 rewrite_urls: bool,
112 ) -> Option<Result<Remote<'_>, find::Error>> {
113 fn config_spec<T: config::tree::keys::Validate>(
114 specs: Vec<std::borrow::Cow<'_, BStr>>,
115 name_or_url: &BStr,
116 key: &'static config::tree::keys::Any<T>,
117 op: gix_refspec::parse::Operation,
118 ) -> Result<Vec<gix_refspec::RefSpec>, find::Error> {
119 let kind = key.name;
120 specs
121 .into_iter()
122 .map(|spec| {
123 key.try_into_refspec(spec, op).map_err(|err| find::Error::RefSpec {
124 remote_name: name_or_url.into(),
125 kind,
126 source: err,
127 })
128 })
129 .collect::<Result<Vec<_>, _>>()
130 .map(|mut specs| {
131 specs.sort();
132 specs.dedup();
133 specs
134 })
135 }
136
137 let mut filter = self.filter_config_section();
138 let name_or_url = name_or_url.into();
139 let mut config_url = |key: &'static config::tree::keys::Url, kind: &'static str| {
140 self.config
141 .resolved
142 .string_filter("remote", Some(name_or_url), key.name, &mut filter)
143 .map(|url| {
144 key.try_into_url(url).map_err(|err| find::Error::Url {
145 kind,
146 remote_name: name_or_url.into(),
147 source: err,
148 })
149 })
150 };
151 let url = config_url(&config::tree::Remote::URL, "fetch");
152 let push_url = config_url(&config::tree::Remote::PUSH_URL, "push");
153 let config = &self.config.resolved;
154
155 let fetch_specs = config
156 .strings_filter("remote", Some(name_or_url), "fetch", &mut filter)
157 .map(|specs| {
158 config_spec(
159 specs,
160 name_or_url,
161 &config::tree::Remote::FETCH,
162 gix_refspec::parse::Operation::Fetch,
163 )
164 });
165 let push_specs = config
166 .strings_filter("remote", Some(name_or_url), "push", &mut filter)
167 .map(|specs| {
168 config_spec(
169 specs,
170 name_or_url,
171 &config::tree::Remote::PUSH,
172 gix_refspec::parse::Operation::Push,
173 )
174 });
175 let fetch_tags = config
176 .string_filter("remote", Some(name_or_url), "tagOpt", &mut filter)
177 .map(|value| {
178 config::tree::Remote::TAG_OPT
179 .try_into_tag_opt(value)
180 .map_err(Into::into)
181 });
182 let fetch_tags = match fetch_tags {
183 Some(Ok(v)) => v,
184 Some(Err(err)) => return Some(Err(err)),
185 None => Default::default(),
186 };
187
188 match (url, fetch_specs, push_url, push_specs) {
189 (None, None, None, None) => None,
190 (None, _, None, _) => Some(Err(find::Error::UrlMissing)),
191 (url, fetch_specs, push_url, push_specs) => {
192 let url = match url {
193 Some(Ok(v)) => Some(v),
194 Some(Err(err)) => return Some(Err(err)),
195 None => None,
196 };
197 let push_url = match push_url {
198 Some(Ok(v)) => Some(v),
199 Some(Err(err)) => return Some(Err(err)),
200 None => None,
201 };
202 let fetch_specs = match fetch_specs {
203 Some(Ok(v)) => v,
204 Some(Err(err)) => return Some(Err(err)),
205 None => Vec::new(),
206 };
207 let push_specs = match push_specs {
208 Some(Ok(v)) => v,
209 Some(Err(err)) => return Some(Err(err)),
210 None => Vec::new(),
211 };
212
213 Some(
214 Remote::from_preparsed_config(
215 Some(name_or_url.to_owned()),
216 url,
217 push_url,
218 fetch_specs,
219 push_specs,
220 rewrite_urls,
221 fetch_tags,
222 self,
223 )
224 .map_err(Into::into),
225 )
226 }
227 }
228 }
229}