use crate::git_system_config::system_attributes_for;
use crate::repo_read_config_scan::ScopeEnvironment;
use crate::repo_read_config_text::parse_bool;
use crate::worktree_attributes::{AttributeLine, CONVERTING_ATTRIBUTES, attribute_name};
use std::collections::{HashMap, HashSet, VecDeque};
use std::ffi::OsString;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SystemAttributes {
Disabled,
File(PathBuf),
Unknown,
}
#[derive(Clone, Debug)]
pub struct ConversionEnvironment {
pub scope: ScopeEnvironment,
pub system_attributes: SystemAttributes,
pub alternate_attribute_source: Option<String>,
pub variables: Vec<(OsString, OsString)>,
}
impl ConversionEnvironment {
pub fn from_variables(variables: &[(OsString, OsString)]) -> ConversionEnvironment {
let selected: Option<String> = if sets_variable(variables, "GIT_ATTR_SOURCE") { Some(String::from("GIT_ATTR_SOURCE is set")) } else { None };
return ConversionEnvironment { scope: ScopeEnvironment::from_variables(variables), system_attributes: system_attributes_from(variables), alternate_attribute_source: selected, variables: variables.to_vec() };
}
}
fn sets_variable(variables: &[(OsString, OsString)], wanted: &str) -> bool {
for (name, _value) in variables {
if name == wanted {
return true;
}
}
return false;
}
pub fn system_attributes_from(variables: &[(OsString, OsString)]) -> SystemAttributes {
let mut switch: Option<&OsString> = None;
for (name, value) in variables.iter().rev() {
if name == "GIT_ATTR_NOSYSTEM" {
switch = Some(value);
break;
}
}
let disabled: Option<bool> = match switch {
None => Some(false),
Some(value) if value.is_empty() => Some(false),
Some(value) => parse_bool(Some(value.as_bytes())),
};
return match disabled {
Some(true) => SystemAttributes::Disabled,
Some(false) => system_attributes_for(variables).map_or(SystemAttributes::Unknown, SystemAttributes::File),
None => SystemAttributes::Unknown,
};
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AttributeFile {
pub label: String,
pub base: Vec<u8>,
pub lines: Vec<AttributeLine>,
}
#[derive(Clone, Debug)]
pub struct FixedSources {
pub files: Vec<AttributeFile>,
pub macros: HashSet<Vec<u8>>,
}
pub fn has_quoted_line(lines: &[AttributeLine]) -> bool {
for line in lines {
if line.quoted {
return true;
}
}
return false;
}
pub fn first_converting_token<'a>(tokens: &'a [Vec<u8>], macros: &HashSet<Vec<u8>>) -> Option<&'a Vec<u8>> {
for token in tokens {
if converts(token.as_slice(), macros) {
return Some(token);
}
}
return None;
}
pub fn converts(token: &[u8], macros: &HashSet<Vec<u8>>) -> bool {
let name: &[u8] = attribute_name(token);
return CONVERTING_ATTRIBUTES.contains(&name) || macros.contains(name);
}
pub fn converting_macros(files: &[AttributeFile]) -> HashSet<Vec<u8>> {
let mut mentioned_by: HashMap<Vec<u8>, Vec<Vec<u8>>> = HashMap::new();
for file in files {
for line in &file.lines {
if !line.is_macro {
continue;
}
for token in &line.tokens {
mentioned_by.entry(attribute_name(token.as_slice()).to_vec()).or_default().push(line.subject.clone());
}
}
}
let mut known: HashSet<Vec<u8>> = HashSet::new();
let mut queue: VecDeque<Vec<u8>> = CONVERTING_ATTRIBUTES.iter().copied().map(<[u8]>::to_vec).collect();
while let Some(name) = queue.pop_front() {
for macro_name in mentioned_by.get(&name).into_iter().flatten() {
if known.insert(macro_name.clone()) {
queue.push_back(macro_name.clone());
}
}
}
return known;
}