summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/remote/save.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/remote/save.rs')
-rw-r--r--vendor/gix/src/remote/save.rs125
1 files changed, 125 insertions, 0 deletions
diff --git a/vendor/gix/src/remote/save.rs b/vendor/gix/src/remote/save.rs
new file mode 100644
index 000000000..0e347551e
--- /dev/null
+++ b/vendor/gix/src/remote/save.rs
@@ -0,0 +1,125 @@
+use std::convert::TryInto;
+
+use crate::{
+ bstr::{BStr, BString},
+ config, remote, Remote,
+};
+
+/// The error returned by [`Remote::save_to()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("The remote pointing to {} is anonymous and can't be saved.", url.to_bstring())]
+ NameMissing { url: gix_url::Url },
+}
+
+/// The error returned by [`Remote::save_as_to()`].
+///
+/// Note that this type should rather be in the `as` module, but cannot be as it's part of the Rust syntax.
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum AsError {
+ #[error(transparent)]
+ Save(#[from] Error),
+ #[error(transparent)]
+ Name(#[from] crate::remote::name::Error),
+}
+
+/// Serialize into gix-config.
+impl Remote<'_> {
+ /// Save ourselves to the given `config` if we are a named remote or fail otherwise.
+ ///
+ /// Note that all sections named `remote "<name>"` will be cleared of all values we are about to write,
+ /// and the last `remote "<name>"` section will be containing all relevant values so that reloading the remote
+ /// from `config` would yield the same in-memory state.
+ pub fn save_to(&self, config: &mut gix_config::File<'static>) -> Result<(), Error> {
+ fn as_key(name: &str) -> gix_config::parse::section::Key<'_> {
+ name.try_into().expect("valid")
+ }
+ let name = self.name().ok_or_else(|| Error::NameMissing {
+ url: self
+ .url
+ .as_ref()
+ .or(self.push_url.as_ref())
+ .expect("one url is always set")
+ .to_owned(),
+ })?;
+ if let Some(section_ids) = config.sections_and_ids_by_name("remote").map(|it| {
+ it.filter_map(|(s, id)| (s.header().subsection_name() == Some(name.as_bstr())).then_some(id))
+ .collect::<Vec<_>>()
+ }) {
+ let mut sections_to_remove = Vec::new();
+ const KEYS_TO_REMOVE: &[&str] = &[
+ config::tree::Remote::URL.name,
+ config::tree::Remote::PUSH_URL.name,
+ config::tree::Remote::FETCH.name,
+ config::tree::Remote::PUSH.name,
+ config::tree::Remote::TAG_OPT.name,
+ ];
+ for id in section_ids {
+ let mut section = config.section_mut_by_id(id).expect("just queried");
+ let was_empty = section.num_values() == 0;
+
+ for key in KEYS_TO_REMOVE {
+ while section.remove(key).is_some() {}
+ }
+
+ let is_empty_after_deletions_of_values_to_be_written = section.num_values() == 0;
+ if !was_empty && is_empty_after_deletions_of_values_to_be_written {
+ sections_to_remove.push(id);
+ }
+ }
+ for id in sections_to_remove {
+ config.remove_section_by_id(id);
+ }
+ }
+ let mut section = config
+ .section_mut_or_create_new("remote", Some(name.as_ref()))
+ .expect("section name is validated and 'remote' is acceptable");
+ if let Some(url) = self.url.as_ref() {
+ section.push(as_key("url"), Some(url.to_bstring().as_ref()));
+ }
+ if let Some(url) = self.push_url.as_ref() {
+ section.push(as_key("pushurl"), Some(url.to_bstring().as_ref()));
+ }
+ if self.fetch_tags != Default::default() {
+ section.push(
+ as_key(config::tree::Remote::TAG_OPT.name),
+ BStr::new(match self.fetch_tags {
+ remote::fetch::Tags::All => "--tags",
+ remote::fetch::Tags::None => "--no-tags",
+ remote::fetch::Tags::Included => unreachable!("BUG: the default shouldn't be written and we try"),
+ })
+ .into(),
+ );
+ }
+ for (key, spec) in self
+ .fetch_specs
+ .iter()
+ .map(|spec| ("fetch", spec))
+ .chain(self.push_specs.iter().map(|spec| ("push", spec)))
+ {
+ section.push(as_key(key), Some(spec.to_ref().to_bstring().as_ref()));
+ }
+ Ok(())
+ }
+
+ /// Forcefully set our name to `name` and write our state to `config` similar to [`save_to()`][Self::save_to()].
+ ///
+ /// Note that this sets a name for anonymous remotes, but overwrites the name for those who were named before.
+ /// If this name is different from the current one, the git configuration will still contain the previous name,
+ /// and the caller should account for that.
+ pub fn save_as_to(
+ &mut self,
+ name: impl Into<BString>,
+ config: &mut gix_config::File<'static>,
+ ) -> Result<(), AsError> {
+ let name = crate::remote::name::validated(name)?;
+ let prev_name = self.name.take();
+ self.name = Some(name.into());
+ self.save_to(config).map_err(|err| {
+ self.name = prev_name;
+ err.into()
+ })
+ }
+}