1use std::path::{Path, PathBuf};
2
3use bstr::{BStr, ByteSlice};
4use gix_glob::search::{pattern, Pattern};
5
6use super::Attributes;
7use crate::{
8 search::{Assignments, MetadataCollection, Outcome, TrackedAssignment, Value},
9 Search,
10};
11
12impl Search {
14 pub fn new_globals(
23 files: impl IntoIterator<Item = impl Into<PathBuf>>,
24 buf: &mut Vec<u8>,
25 collection: &mut MetadataCollection,
26 ) -> std::io::Result<Self> {
27 let mut group = Self::default();
28 group.add_patterns_buffer(
29 b"[attr]binary -diff -merge -text",
30 "[builtin]".into(),
31 None,
32 collection,
33 true, );
35
36 for path in files.into_iter() {
37 group.add_patterns_file(path.into(), true, None, buf, collection, true )?;
38 }
39 Ok(group)
40 }
41}
42
43impl Search {
45 pub fn add_patterns_file(
51 &mut self,
52 source: PathBuf,
53 follow_symlinks: bool,
54 root: Option<&Path>,
55 buf: &mut Vec<u8>,
56 collection: &mut MetadataCollection,
57 allow_macros: bool,
58 ) -> std::io::Result<bool> {
59 let was_added = gix_glob::search::add_patterns_file(&mut self.patterns, source, follow_symlinks, root, buf)?;
62 if was_added {
63 let last = self.patterns.last_mut().expect("just added");
64 if !allow_macros {
65 last.patterns
66 .retain(|p| !matches!(p.value, Value::MacroAssignments { .. }));
67 }
68 collection.update_from_list(last);
69 }
70 Ok(was_added)
71 }
72 pub fn add_patterns_buffer(
76 &mut self,
77 bytes: &[u8],
78 source: PathBuf,
79 root: Option<&Path>,
80 collection: &mut MetadataCollection,
81 allow_macros: bool,
82 ) {
83 self.patterns.push(pattern::List::from_bytes(bytes, source, root));
84 let last = self.patterns.last_mut().expect("just added");
85 if !allow_macros {
86 last.patterns
87 .retain(|p| !matches!(p.value, Value::MacroAssignments { .. }));
88 }
89 collection.update_from_list(last);
90 }
91
92 pub fn pop_pattern_list(&mut self) -> Option<gix_glob::search::pattern::List<Attributes>> {
94 self.patterns.pop()
95 }
96}
97
98impl Search {
100 pub fn pattern_matching_relative_path(
103 &self,
104 relative_path: &BStr,
105 case: gix_glob::pattern::Case,
106 is_dir: Option<bool>,
107 out: &mut Outcome,
108 ) -> bool {
109 let basename_pos = relative_path.rfind(b"/").map(|p| p + 1);
110 let mut has_match = false;
111 self.patterns.iter().rev().any(|pl| {
112 has_match |= pattern_matching_relative_path(pl, relative_path, basename_pos, case, is_dir, out);
113 out.is_done()
114 });
115 has_match
116 }
117
118 pub fn num_pattern_lists(&self) -> usize {
120 self.patterns.len()
121 }
122}
123
124impl Pattern for Attributes {
125 type Value = Value;
126
127 fn bytes_to_patterns(bytes: &[u8], _source: &std::path::Path) -> Vec<pattern::Mapping<Self::Value>> {
128 fn into_owned_assignments<'a>(
129 attrs: impl Iterator<Item = Result<crate::AssignmentRef<'a>, crate::name::Error>>,
130 ) -> Option<Assignments> {
131 let res = attrs
132 .map(|res| {
133 res.map(|a| TrackedAssignment {
134 id: Default::default(),
135 inner: a.to_owned(),
136 })
137 })
138 .collect::<Result<Assignments, _>>();
139 match res {
140 Ok(res) => Some(res),
141 Err(_err) => {
142 gix_trace::warn!("{}", _err);
143 None
144 }
145 }
146 }
147
148 crate::parse(bytes)
149 .filter_map(|res| match res {
150 Ok(pattern) => Some(pattern),
151 Err(_err) => {
152 gix_trace::warn!("{}: {}", _source.display(), _err);
153 None
154 }
155 })
156 .filter_map(|(pattern_kind, assignments, line_number)| {
157 let (pattern, value) = match pattern_kind {
158 crate::parse::Kind::Macro(macro_name) => (
159 gix_glob::Pattern {
160 text: macro_name.as_str().into(),
161 mode: macro_mode(),
162 first_wildcard_pos: None,
163 },
164 Value::MacroAssignments {
165 id: Default::default(),
166 assignments: into_owned_assignments(assignments)?,
167 },
168 ),
169 crate::parse::Kind::Pattern(p) => (
170 (!p.is_negative()).then_some(p)?,
171 Value::Assignments(into_owned_assignments(assignments)?),
172 ),
173 };
174 pattern::Mapping {
175 pattern,
176 value,
177 sequence_number: line_number,
178 }
179 .into()
180 })
181 .collect()
182 }
183}
184
185impl Attributes {
186 fn may_use_glob_pattern(pattern: &gix_glob::Pattern) -> bool {
187 pattern.mode != macro_mode()
188 }
189}
190
191fn macro_mode() -> gix_glob::pattern::Mode {
192 gix_glob::pattern::Mode::all()
193}
194
195#[allow(unused_variables)]
201fn pattern_matching_relative_path(
202 list: &gix_glob::search::pattern::List<Attributes>,
203 relative_path: &BStr,
204 basename_pos: Option<usize>,
205 case: gix_glob::pattern::Case,
206 is_dir: Option<bool>,
207 out: &mut Outcome,
208) -> bool {
209 let (relative_path, basename_start_pos) =
210 match list.strip_base_handle_recompute_basename_pos(relative_path, basename_pos, case) {
211 Some(r) => r,
212 None => return false,
213 };
214 let cur_len = out.remaining();
215 'outer: for pattern::Mapping {
216 pattern,
217 value,
218 sequence_number,
219 } in list
220 .patterns
221 .iter()
222 .rev()
223 .filter(|pm| Attributes::may_use_glob_pattern(&pm.pattern))
224 {
225 let value: &Value = value;
226 let attrs = match value {
227 Value::MacroAssignments { .. } => {
228 unreachable!("we can't match on macros as they have no pattern")
229 }
230 Value::Assignments(attrs) => attrs,
231 };
232 if out.has_unspecified_attributes(attrs.iter().map(|attr| attr.id))
233 && pattern.matches_repo_relative_path(
234 relative_path,
235 basename_start_pos,
236 is_dir,
237 case,
238 gix_glob::wildmatch::Mode::NO_MATCH_SLASH_LITERAL,
239 )
240 {
241 let all_filled = out.fill_attributes(attrs.iter(), pattern, list.source.as_ref(), *sequence_number);
242 if all_filled {
243 break 'outer;
244 }
245 }
246 }
247 cur_len != out.remaining()
248}