Sebastian Thielmake fmtea2136b4 years agoHistory
236 lines · 10.0 KB · Rust
RawDownload
use std::{borrow::Cow, convert::TryInto, path::PathBuf, time::Duration}; use git_lock::acquire::Fail; use crate::{    config::{cache::util::check_lenient_default, checkout_options, Cache},    remote,    repository::identity,}; /// Accessimpl Cache {    pub(crate) fn diff_algorithm(&self) -> Result<git_diff::text::Algorithm, crate::config::diff::algorithm::Error> {        use crate::config::diff::algorithm::Error;        self.diff_algorithm            .get_or_try_init(|| {                let res = {                    let name = self                        .resolved                        .string("diff", None, "algorithm")                        .unwrap_or_else(|| Cow::Borrowed("myers".into()));                    if name.eq_ignore_ascii_case(b"myers") || name.eq_ignore_ascii_case(b"default") {                        Ok(git_diff::text::Algorithm::Myers)                    } else if name.eq_ignore_ascii_case(b"minimal") {                        Ok(git_diff::text::Algorithm::MyersMinimal)                    } else if name.eq_ignore_ascii_case(b"histogram") {                        Ok(git_diff::text::Algorithm::Histogram)                    } else if name.eq_ignore_ascii_case(b"patience") {                        if self.lenient_config {                            Ok(git_diff::text::Algorithm::Histogram)                        } else {                            Err(Error::Unimplemented {                                name: name.into_owned(),                            })                        }                    } else {                        Err(Error::Unknown {                            name: name.into_owned(),                        })                    }                };                check_lenient_default(res, self.lenient_config, || git_diff::text::Algorithm::Myers)            })            .copied()    }     pub(crate) fn personas(&self) -> &identity::Personas {        self.personas            .get_or_init(|| identity::Personas::from_config_and_env(&self.resolved, self.git_prefix))    }     pub(crate) fn url_rewrite(&self) -> &remote::url::Rewrite {        self.url_rewrite            .get_or_init(|| remote::url::Rewrite::from_config(&self.resolved, self.filter_config_section))    }     #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]    pub(crate) fn url_scheme(        &self,    ) -> Result<&remote::url::SchemePermission, remote::url::scheme_permission::init::Error> {        self.url_scheme.get_or_try_init(|| {            remote::url::SchemePermission::from_config(&self.resolved, self.git_prefix, self.filter_config_section)        })    }     /// Returns (file-timeout, pack-refs timeout)    pub(crate) fn lock_timeout(        &self,    ) -> Result<(git_lock::acquire::Fail, git_lock::acquire::Fail), git_config::value::Error> {        enum Kind {            RefFiles,            RefPackFile,        }        let mut out: [git_lock::acquire::Fail; 2] = Default::default();         for (idx, kind) in [Kind::RefFiles, Kind::RefPackFile].iter().enumerate() {            let (key, default_ms) = match kind {                Kind::RefFiles => ("filesRefLockTimeout", 100),                Kind::RefPackFile => ("packedRefsTimeout", 1000),            };            let mk_default = || Fail::AfterDurationWithBackoff(Duration::from_millis(default_ms));            let mut fnp = self.filter_config_section;             let lock_mode = match self.resolved.integer_filter("core", None, key, &mut fnp) {                Some(Ok(val)) if val < 0 => Fail::AfterDurationWithBackoff(Duration::from_secs(u64::MAX)),                Some(Ok(val)) if val == 0 => Fail::Immediately,                Some(Ok(val)) => Fail::AfterDurationWithBackoff(Duration::from_millis(                    val.try_into().expect("i64 can be repsented by u64"),                )),                Some(Err(_)) if self.lenient_config => mk_default(),                Some(Err(err)) => return Err(err),                None => mk_default(),            };            out[idx] = lock_mode;        }        Ok((out[0], out[1]))    }     /// The path to the user-level excludes file to ignore certain files in the worktree.    pub(crate) fn excludes_file(&self) -> Option<Result<PathBuf, git_config::path::interpolate::Error>> {        self.trusted_file_path("core", None, "excludesFile")?            .map(|p| p.into_owned())            .into()    }     /// A helper to obtain a file from trusted configuration at `section_name`, `subsection_name`, and `key`, which is interpolated    /// if present.    pub(crate) fn trusted_file_path(        &self,        section_name: impl AsRef<str>,        subsection_name: Option<&str>,        key: impl AsRef<str>,    ) -> Option<Result<Cow<'_, std::path::Path>, git_config::path::interpolate::Error>> {        let path = self.resolved.path_filter(            section_name,            subsection_name,            key,            &mut self.filter_config_section.clone(),        )?;         let install_dir = crate::path::install_dir().ok();        let home = self.home_dir();        let ctx = crate::config::cache::interpolate_context(install_dir.as_deref(), home.as_deref());        Some(path.interpolate(ctx))    }     pub(crate) fn apply_leniency<T, E>(&self, res: Option<Result<T, E>>) -> Result<Option<T>, E> {        match res {            Some(Ok(v)) => Ok(Some(v)),            Some(Err(_err)) if self.lenient_config => Ok(None),            Some(Err(err)) => Err(err),            None => Ok(None),        }    }     /// Collect everything needed to checkout files into a worktree.    /// Note that some of the options being returned will be defaulted so safe settings, the caller might have to override them    /// depending on the use-case.    pub(crate) fn checkout_options(        &self,        git_dir: &std::path::Path,    ) -> Result<git_worktree::index::checkout::Options, checkout_options::Error> {        fn checkout_thread_limit_from_config(            config: &git_config::File<'static>,        ) -> Option<Result<usize, checkout_options::Error>> {            config.integer("checkout", None, "workers").map(|val| match val {                Ok(v) if v < 0 => Ok(0),                Ok(v) => Ok(v.try_into().expect("positive i64 can always be usize on 64 bit")),                Err(err) => Err(checkout_options::Error::Configuration {                    key: "checkout.workers",                    source: err,                }),            })        }         fn boolean(me: &Cache, full_key: &'static str, default: bool) -> Result<bool, checkout_options::Error> {            let mut tokens = full_key.split('.');            let section = tokens.next().expect("section");            let key = tokens.next().expect("key");            assert!(tokens.next().is_none(), "core.<key>");            Ok(me                .apply_leniency(me.resolved.boolean(section, None, key))                .map_err(|err| checkout_options::Error::Configuration {                    key: full_key,                    source: err,                })?                .unwrap_or(default))        }         fn assemble_attribute_globals(            me: &Cache,            _git_dir: &std::path::Path,        ) -> Result<git_attributes::MatchGroup, checkout_options::Error> {            let _attributes_file = match me.trusted_file_path("core", None, "attributesFile").transpose()? {                Some(attributes) => Some(attributes.into_owned()),                None => me.xdg_config_path("attributes").ok().flatten(),            };            // TODO: implement git_attributes::MatchGroup::<git_attributes::Attributes>::from_git_dir(), similar to what's done for `Ignore`.            Ok(Default::default())        }         let thread_limit = self.apply_leniency(checkout_thread_limit_from_config(&self.resolved))?;        Ok(git_worktree::index::checkout::Options {            fs: git_worktree::fs::Capabilities {                precompose_unicode: boolean(self, "core.precomposeUnicode", false)?,                ignore_case: boolean(self, "core.ignoreCase", false)?,                executable_bit: boolean(self, "core.fileMode", true)?,                symlink: boolean(self, "core.symlinks", true)?,            },            thread_limit,            destination_is_initially_empty: false,            overwrite_existing: false,            keep_going: false,            trust_ctime: boolean(self, "core.trustCTime", true)?,            check_stat: self                .resolved                .string("core", None, "checkStat")                .map_or(true, |v| v.as_ref() != "minimal"),            attribute_globals: assemble_attribute_globals(self, git_dir)?,        })    }    pub(crate) fn xdg_config_path(        &self,        resource_file_name: &str,    ) -> Result<Option<PathBuf>, git_sec::permission::Error<PathBuf>> {        std::env::var_os("XDG_CONFIG_HOME")            .map(|path| (PathBuf::from(path), &self.xdg_config_home_env))            .or_else(|| {                std::env::var_os("HOME").map(|path| {                    (                        {                            let mut p = PathBuf::from(path);                            p.push(".config");                            p                        },                        &self.home_env,                    )                })            })            .and_then(|(base, permission)| {                let resource = base.join("git").join(resource_file_name);                permission.check(resource).transpose()            })            .transpose()    }     /// Return the home directory if we are allowed to read it and if it is set in the environment.    ///    /// We never fail for here even if the permission is set to deny as we `git-config` will fail later    /// if it actually wants to use the home directory - we don't want to fail prematurely.    pub(crate) fn home_dir(&self) -> Option<PathBuf> {        std::env::var_os("HOME")            .map(PathBuf::from)            .and_then(|path| self.home_env.check_opt(path))    }}