use crate::repo_read_config_list::ListedEntry;
use crate::repo_read_config_text::parse_bool;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UntrackedMode {
Keep,
Write,
Remove,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IndexSettings {
pub monitor: bool,
pub untracked: UntrackedMode,
pub shared_repository: i32,
}
const DECLINED: [&str; 8] = [
"core.splitindex",
"index.sparse",
"index.skiphash",
"index.recordendofindexentries",
"index.recordoffsettable",
"index.threads",
"core.hookspath",
"core.worktree",
];
const DECLINED_WHEN_TRUE: [&str; 3] = ["core.sparsecheckout", "core.ignorecase", "feature.manyfiles"];
const SHARED_GROUP: i32 = 0o660;
const SHARED_EVERYBODY: i32 = 0o664;
pub fn shared_permission(value: Option<&[u8]>) -> Option<i32> {
let Some(text) = value else {
return Some(SHARED_GROUP);
};
match text {
b"" | b"umask" => return Some(0),
b"group" => return Some(SHARED_GROUP),
b"all" | b"world" | b"everybody" => return Some(SHARED_EVERYBODY),
_ => {}
}
let digits: &str = std::str::from_utf8(text).ok()?;
if let Ok(number) = i32::from_str_radix(digits, 8) {
return match number {
0 => Some(0),
1 => Some(SHARED_GROUP),
2 => Some(SHARED_EVERYBODY),
other if (other & 0o600) != 0o600 || other < 0 => None,
other => Some(-(other & 0o666)),
};
}
return parse_bool(Some(text)).map(|truth| { return if truth { SHARED_GROUP } else { 0 }; });
}
pub fn read_settings(listed: &[ListedEntry]) -> Option<IndexSettings> {
let last = |key: &str| -> Option<&crate::repo_read_config_text::ConfigEntry> {
return listed.iter().rev().map(|item| { return &item.entry; }).find(|entry| { return entry.key == key; });
};
if listed.iter().any(|item| { return DECLINED.contains(&item.entry.key.as_str()); }) {
return None;
}
for key in DECLINED_WHEN_TRUE {
if let Some(entry) = last(key)
&& parse_bool(entry.value.as_deref()) != Some(false)
{
return None;
}
}
let monitor: bool = match last("core.fsmonitor") {
None => false,
Some(entry) => match entry.value.as_deref() {
None => true,
Some(text) => match parse_bool(Some(text)) {
Some(truth) => truth,
None => !text.is_empty(),
},
},
};
let untracked: UntrackedMode = match last("core.untrackedcache") {
None => UntrackedMode::Keep,
Some(entry) => match entry.value.as_deref() {
Some(text) if text.eq_ignore_ascii_case(b"keep") => UntrackedMode::Keep,
other => match parse_bool(other)? {
true => UntrackedMode::Write,
false => UntrackedMode::Remove,
},
},
};
let shared_repository: i32 = match last("core.sharedrepository") {
None => 0,
Some(entry) => shared_permission(entry.value.as_deref())?,
};
return Some(IndexSettings { monitor, untracked, shared_repository });
}
#[cfg(test)]
#[path = "index_settings_tests.rs"]
mod tests;