use std::path::{Path, PathBuf};
use bstr::{BStr, ByteSlice};
use gix_glob::search::{pattern, Pattern};
use super::Attributes;
use crate::{
search::{Assignments, MetadataCollection, Outcome, TrackedAssignment, Value},
Search,
};
impl Search {
pub fn new_globals(
files: impl IntoIterator<Item = impl Into<PathBuf>>,
buf: &mut Vec<u8>,
collection: &mut MetadataCollection,
) -> std::io::Result<Self> {
let mut group = Self::default();
group.add_patterns_buffer(
b"[attr]binary -diff -merge -text",
"[builtin]".into(),
None,
collection,
true,
);
for path in files.into_iter() {
group.add_patterns_file(path.into(), true, None, buf, collection, true )?;
}
Ok(group)
}
}
impl Search {
pub fn add_patterns_file(
&mut self,
source: PathBuf,
follow_symlinks: bool,
root: Option<&Path>,
buf: &mut Vec<u8>,
collection: &mut MetadataCollection,
allow_macros: bool,
) -> std::io::Result<bool> {
let was_added =
gix_glob::search::add_patterns_file(&mut self.patterns, source, follow_symlinks, root, buf, Attributes)?;
if was_added {
let last = self.patterns.last_mut().expect("just added");
if !allow_macros {
last.patterns
.retain(|p| !matches!(p.value, Value::MacroAssignments { .. }));
}
collection.update_from_list(last);
}
Ok(was_added)
}
pub fn add_patterns_buffer(
&mut self,
bytes: &[u8],
source: PathBuf,
root: Option<&Path>,
collection: &mut MetadataCollection,
allow_macros: bool,
) {
self.patterns
.push(pattern::List::from_bytes(bytes, source, root, Attributes));
let last = self.patterns.last_mut().expect("just added");
if !allow_macros {
last.patterns
.retain(|p| !matches!(p.value, Value::MacroAssignments { .. }));
}
collection.update_from_list(last);
}
pub fn pop_pattern_list(&mut self) -> Option<gix_glob::search::pattern::List<Attributes>> {
self.patterns.pop()
}
}
impl Search {
pub fn pattern_matching_relative_path(
&self,
relative_path: &BStr,
case: gix_glob::pattern::Case,
is_dir: Option<bool>,
out: &mut Outcome,
) -> bool {
let basename_pos = relative_path.rfind(b"/").map(|p| p + 1);
let mut has_match = false;
self.patterns.iter().rev().any(|pl| {
has_match |= pattern_matching_relative_path(pl, relative_path, basename_pos, case, is_dir, out);
out.is_done()
});
has_match
}
pub fn num_pattern_lists(&self) -> usize {
self.patterns.len()
}
}
impl Pattern for Attributes {
type Value = Value;
fn bytes_to_patterns(&self, bytes: &[u8], _source: &std::path::Path) -> Vec<pattern::Mapping<Self::Value>> {
fn into_owned_assignments<'a>(
attrs: impl Iterator<Item = Result<crate::AssignmentRef<'a>, crate::name::Error>>,
) -> Option<Assignments> {
let res = attrs
.map(|res| {
res.map(|a| TrackedAssignment {
id: Default::default(),
inner: a.to_owned(),
})
})
.collect::<Result<Assignments, _>>();
match res {
Ok(res) => Some(res),
Err(_err) => {
gix_trace::warn!("{}", _err);
None
}
}
}
crate::parse(bytes)
.filter_map(|res| match res {
Ok(pattern) => Some(pattern),
Err(_err) => {
gix_trace::warn!("{}: {}", _source.display(), _err);
None
}
})
.filter_map(|(pattern_kind, assignments, line_number)| {
let (pattern, value) = match pattern_kind {
crate::parse::Kind::Macro(macro_name) => (
gix_glob::Pattern {
text: macro_name.as_str().into(),
mode: macro_mode(),
first_wildcard_pos: None,
},
Value::MacroAssignments {
id: Default::default(),
assignments: into_owned_assignments(assignments)?,
},
),
crate::parse::Kind::Pattern(p) => (
(!p.is_negative()).then_some(p)?,
Value::Assignments(into_owned_assignments(assignments)?),
),
};
pattern::Mapping {
pattern,
value,
sequence_number: line_number,
}
.into()
})
.collect()
}
}
impl Attributes {
fn may_use_glob_pattern(pattern: &gix_glob::Pattern) -> bool {
pattern.mode != macro_mode()
}
}
fn macro_mode() -> gix_glob::pattern::Mode {
gix_glob::pattern::Mode::all()
}
#[allow(unused_variables)]
fn pattern_matching_relative_path(
list: &gix_glob::search::pattern::List<Attributes>,
relative_path: &BStr,
basename_pos: Option<usize>,
case: gix_glob::pattern::Case,
is_dir: Option<bool>,
out: &mut Outcome,
) -> bool {
let (relative_path, basename_start_pos) =
match list.strip_base_handle_recompute_basename_pos(relative_path, basename_pos, case) {
Some(r) => r,
None => return false,
};
let cur_len = out.remaining();
'outer: for pattern::Mapping {
pattern,
value,
sequence_number,
} in list
.patterns
.iter()
.rev()
.filter(|pm| Attributes::may_use_glob_pattern(&pm.pattern))
{
let value: &Value = value;
let attrs = match value {
Value::MacroAssignments { .. } => {
unreachable!("we can't match on macros as they have no pattern")
}
Value::Assignments(attrs) => attrs,
};
if out.has_unspecified_attributes(attrs.iter().map(|attr| attr.id))
&& pattern.matches_repo_relative_path(
relative_path,
basename_start_pos,
is_dir,
case,
gix_glob::wildmatch::Mode::NO_MATCH_SLASH_LITERAL,
)
{
let all_filled = out.fill_attributes(attrs.iter(), pattern, list.source.as_ref(), *sequence_number);
if all_filled {
break 'outer;
}
}
}
cur_len != out.remaining()
}