pub use gix_config::*;
use gix_features::threading::OnceCell;
use crate::{Repository, repository::identity};
pub(crate) mod cache;
pub mod file_mut;
mod snapshot;
#[cfg(feature = "credentials")]
pub use snapshot::credential_helpers;
pub mod overrides;
pub mod tree;
pub use tree::root::Tree;
pub struct FileTransaction {
pub(crate) lock: gix_lock::File,
pub(crate) config: gix_config::File,
}
pub struct Snapshot<'repo> {
pub repo: &'repo Repository,
}
pub struct SnapshotMut<'repo> {
pub repo: Option<&'repo mut Repository>,
pub(crate) config: gix_config::File,
}
pub struct CommitAutoRollback<'repo> {
pub repo: Option<&'repo mut Repository>,
pub(crate) prev_config: crate::Config,
}
pub mod section {
pub fn is_trusted(meta: &gix_config::file::Metadata) -> bool {
meta.trust == gix_sec::Trust::Full || meta.source.kind() != gix_config::source::Kind::Repository
}
}
pub mod diff {
pub mod algorithm {
use crate::bstr::BString;
#[derive(Debug)]
#[expect(missing_docs)]
pub enum Error {
Unknown { name: BString },
Unimplemented { name: BString },
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Unknown { name } => write!(f, "Unknown diff algorithm named '{name}'"),
Error::Unimplemented { name } => write!(f, "The '{name}' algorithm is not yet implemented"),
}
}
}
impl std::error::Error for Error {}
}
}
pub(crate) mod key {
use gix_error::{Message, MetadataValue};
use super::tree;
pub fn error(key: &dyn tree::Key, message: &'static str) -> Message {
let mut error = gix_error::validation(message).with("key", key.logical_name());
if let Some(environment) = key.environment_override() {
error = error.with("environment_override", environment);
}
error
}
pub fn error_with_value(key: &dyn tree::Key, message: &'static str, value: impl Into<MetadataValue>) -> Message {
error(key, message).with("input", value)
}
#[cfg(test)]
mod tests {
use super::{MetadataValue, error, error_with_value};
use crate::{
Error,
bstr::ByteSlice,
config::tree::{Core, Key, Remote, keys},
};
#[test]
fn helpers_cover_key_value_and_environment_metadata() {
let fallback = keys::Any::new("fallback", &Core).with_fallback(&Core::DELTA_BASE_CACHE_LIMIT);
for (key, name, environment) in [
(&Core::BARE as &dyn Key, "core.bare", None),
(&Remote::URL, "remote.<name>.url", None),
(
&Core::DELTA_BASE_CACHE_LIMIT,
"core.deltaBaseCacheLimit",
Some("GIX_PACK_CACHE_MEMORY"),
),
(&fallback, "core.fallback", Some("GIX_PACK_CACHE_MEMORY")),
] {
let empty = b"".as_bstr();
for (message, input) in [
(error(key, "Invalid configuration value"), None),
(
error_with_value(key, "Invalid configuration value", empty),
Some(MetadataValue::from(empty)),
),
] {
let error = Error::from_error(message);
assert!(
error.is_validation(),
"invalid configuration is classified as validation"
);
let metadata = error.metadata().next().expect("a configuration error has metadata");
assert_eq!(
metadata.get("key"),
Some(&MetadataValue::from(name)),
"the context identifies the key"
);
assert_eq!(
metadata.get("input"),
input.as_ref(),
"the context retains the available input"
);
assert_eq!(
metadata.get("environment_override"),
environment.map(MetadataValue::from).as_ref(),
"the context identifies the possible environment override"
);
}
}
assert_eq!(
error_with_value(&Core::BARE, "Invalid boolean", b"bad".as_bstr()).to_string(),
"Invalid boolean, \"input\"=\"bad\", \"key\"=\"core.bare\"",
"the standard message display includes its diagnostic metadata"
);
}
}
}
#[derive(Clone)]
pub(crate) struct Cache {
pub resolved: crate::Config,
pub hex_len: Option<usize>,
pub is_bare: Option<bool>,
pub object_hash: gix_hash::Kind,
pub use_multi_pack_index: bool,
pub reflog: Option<gix_ref::store::WriteReflog>,
pub refs_namespace: Option<gix_ref::Namespace>,
pub(crate) user_agent: OnceCell<String>,
pub(crate) personas: OnceCell<identity::Personas>,
pub(crate) url_rewrite: OnceCell<crate::remote::url::Rewrite>,
#[cfg(feature = "blob-diff")]
pub(crate) diff_renames: OnceCell<(Option<crate::diff::Rewrites>, bool)>,
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
pub(crate) url_scheme: OnceCell<crate::remote::url::SchemePermission>,
#[cfg(feature = "blob-diff")]
pub(crate) diff_algorithm: OnceCell<gix_diff::blob::Algorithm>,
pub(crate) pack_cache_bytes: Option<usize>,
pub(crate) object_cache_bytes: usize,
pub(crate) alloc_limit_bytes: Option<usize>,
pub(crate) loose_compression: gix_zlib::Compression,
pub(crate) static_pack_cache_limit_bytes: Option<usize>,
filter_config_section: fn(&gix_config::file::Metadata) -> bool,
#[cfg(feature = "revision")]
pub object_kind_hint: Option<crate::revision::spec::parse::ObjectKindHint>,
pub ignore_case: bool,
pub lenient_config: bool,
#[cfg_attr(not(feature = "worktree-mutation"), allow(dead_code))]
attributes: crate::open::permissions::Attributes,
environment: crate::open::permissions::Environment,
}
pub(crate) mod shared {
use crate::Result;
use crate::config::{cache::util::ApplyLeniency, tree::Core};
pub fn is_replace_refs_enabled(
config: &gix_config::File,
lenient: bool,
mut filter_config_section: fn(&gix_config::file::Metadata) -> bool,
) -> Result<Option<bool>> {
Core::USE_REPLACE_REFS
.enrich_error(config.boolean_filter("core.useReplaceRefs", &mut filter_config_section))
.with_leniency(lenient)
}
}