Mercurial > public > mercurial-scm > hg-stable
annotate rust/hg-core/src/sparse.rs @ 52769:1b7a57a5b47a
rust: add safe bindings to bdiff.c
I wrote C FFI bindings manually rather than using a bindgen build step because
there are only 2 structs and 3 functions and they're not going to change.
Note that the relative path in build.rs means that cargo publish will no longer
work. If in the future we want to publish to crates.io, we would probably need
to add a Makefile step that copies bdiff sources into the hg-core crate.
author | Mitchell Kember <mkember@janestreet.com> |
---|---|
date | Wed, 18 Dec 2024 10:35:01 -0500 |
parents | 22d24f6d6411 |
children | 94e2547e6f3d |
rev | line source |
---|---|
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
1 use std::{collections::HashSet, fmt::Display, path::Path}; |
49499 | 2 |
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
3 use format_bytes::{format_bytes, write_bytes, DisplayBytes}; |
49499 | 4 |
5 use crate::{ | |
6 errors::HgError, | |
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
7 exit_codes::STATE_ERROR, |
52339
22d24f6d6411
rust-lib: remove exports for not too common pattern-related types
Rapha?l Gom?s <rgomes@octobus.net>
parents:
52338
diff
changeset
|
8 filepatterns::{ |
22d24f6d6411
rust-lib: remove exports for not too common pattern-related types
Rapha?l Gom?s <rgomes@octobus.net>
parents:
52338
diff
changeset
|
9 parse_pattern_file_contents, IgnorePattern, PatternError, |
22d24f6d6411
rust-lib: remove exports for not too common pattern-related types
Rapha?l Gom?s <rgomes@octobus.net>
parents:
52338
diff
changeset
|
10 PatternFileWarning, PatternSyntax, |
22d24f6d6411
rust-lib: remove exports for not too common pattern-related types
Rapha?l Gom?s <rgomes@octobus.net>
parents:
52338
diff
changeset
|
11 }, |
49499 | 12 matchers::{ |
13 AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher, | |
14 UnionMatcher, | |
15 }, | |
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
16 narrow::VALID_PREFIXES, |
49499 | 17 operations::cat, |
18 repo::Repo, | |
19 requirements::SPARSE_REQUIREMENT, | |
20 utils::{hg_path::HgPath, SliceExt}, | |
52339
22d24f6d6411
rust-lib: remove exports for not too common pattern-related types
Rapha?l Gom?s <rgomes@octobus.net>
parents:
52338
diff
changeset
|
21 Revision, NULL_REVISION, |
49499 | 22 }; |
23 | |
24 /// Command which is triggering the config read | |
25 #[derive(Copy, Clone, Debug)] | |
26 pub enum SparseConfigContext { | |
27 Sparse, | |
28 Narrow, | |
29 } | |
30 | |
31 impl DisplayBytes for SparseConfigContext { | |
32 fn display_bytes( | |
33 &self, | |
34 output: &mut dyn std::io::Write, | |
35 ) -> std::io::Result<()> { | |
36 match self { | |
37 SparseConfigContext::Sparse => write_bytes!(output, b"sparse"), | |
38 SparseConfigContext::Narrow => write_bytes!(output, b"narrow"), | |
39 } | |
40 } | |
41 } | |
42 | |
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
43 impl Display for SparseConfigContext { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
45 match self { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
46 SparseConfigContext::Sparse => write!(f, "sparse"), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
47 SparseConfigContext::Narrow => write!(f, "narrow"), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
48 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
49 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
50 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
51 |
49499 | 52 /// Possible warnings when reading sparse configuration |
53 #[derive(Debug, derive_more::From)] | |
54 pub enum SparseWarning { | |
55 /// Warns about improper paths that start with "/" | |
56 RootWarning { | |
57 context: SparseConfigContext, | |
58 line: Vec<u8>, | |
59 }, | |
60 /// Warns about a profile missing from the given changelog revision | |
61 ProfileNotFound { profile: Vec<u8>, rev: Revision }, | |
62 #[from] | |
63 Pattern(PatternFileWarning), | |
64 } | |
65 | |
66 /// Parsed sparse config | |
67 #[derive(Debug, Default)] | |
68 pub struct SparseConfig { | |
69 // Line-separated | |
49502
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
70 pub(crate) includes: Vec<u8>, |
49499 | 71 // Line-separated |
49502
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
72 pub(crate) excludes: Vec<u8>, |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
73 pub(crate) profiles: HashSet<Vec<u8>>, |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
74 pub(crate) warnings: Vec<SparseWarning>, |
49499 | 75 } |
76 | |
49502
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
77 /// All possible errors when reading sparse/narrow config |
49499 | 78 #[derive(Debug, derive_more::From)] |
79 pub enum SparseConfigError { | |
80 IncludesAfterExcludes { | |
81 context: SparseConfigContext, | |
82 }, | |
83 EntryOutsideSection { | |
84 context: SparseConfigContext, | |
85 line: Vec<u8>, | |
86 }, | |
49502
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
87 /// Narrow config does not support '%include' directives |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
88 IncludesInNarrow, |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
89 /// An invalid pattern prefix was given to the narrow spec. Includes the |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
90 /// entire pattern for context. |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
91 InvalidNarrowPrefix(Vec<u8>), |
49499 | 92 #[from] |
93 HgError(HgError), | |
94 #[from] | |
95 PatternError(PatternError), | |
96 } | |
97 | |
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
98 impl From<SparseConfigError> for HgError { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
99 fn from(value: SparseConfigError) -> Self { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
100 match value { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
101 SparseConfigError::IncludesAfterExcludes { context } => { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
102 HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
103 message: format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
104 "{} config cannot have includes after excludes", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
105 context, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
106 ), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
107 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
108 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
109 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
110 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
111 SparseConfigError::EntryOutsideSection { context, line } => { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
112 HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
113 message: format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
114 "{} config entry outside of section: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
115 context, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
116 String::from_utf8_lossy(&line) |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
117 ), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
118 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
119 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
120 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
121 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
122 SparseConfigError::IncludesInNarrow => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
123 message: "including other spec files using '%include' is not \ |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
124 supported in narrowspec" |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
125 .to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
126 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
127 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
128 }, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
129 SparseConfigError::InvalidNarrowPrefix(vec) => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
130 message: String::from_utf8_lossy(&format_bytes!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
131 b"invalid prefix on narrow pattern: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
132 vec |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
133 )) |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
134 .to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
135 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
136 hint: Some(format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
137 "narrow patterns must begin with one of the following: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
138 VALID_PREFIXES.join(", ") |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
139 )), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
140 }, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
141 SparseConfigError::HgError(hg_error) => hg_error, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
142 SparseConfigError::PatternError(pattern_error) => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
143 message: pattern_error.to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
144 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
145 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
146 }, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
147 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
148 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
149 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
150 |
49499 | 151 /// Parse sparse config file content. |
49502
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49499
diff
changeset
|
152 pub(crate) fn parse_config( |
49499 | 153 raw: &[u8], |
154 context: SparseConfigContext, | |
155 ) -> Result<SparseConfig, SparseConfigError> { | |
156 let mut includes = vec![]; | |
157 let mut excludes = vec![]; | |
158 let mut profiles = HashSet::new(); | |
159 let mut warnings = vec![]; | |
160 | |
161 #[derive(PartialEq, Eq)] | |
162 enum Current { | |
163 Includes, | |
164 Excludes, | |
165 None, | |
49522
52464a20add0
rhg: parallellize computation of [unsure_is_modified]
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49502
diff
changeset
|
166 } |
49499 | 167 |
168 let mut current = Current::None; | |
169 let mut in_section = false; | |
170 | |
171 for line in raw.split(|c| *c == b'\n') { | |
172 let line = line.trim(); | |
173 if line.is_empty() || line[0] == b'#' { | |
174 // empty or comment line, skip | |
175 continue; | |
176 } | |
177 if line.starts_with(b"%include ") { | |
178 let profile = line[b"%include ".len()..].trim(); | |
179 if !profile.is_empty() { | |
180 profiles.insert(profile.into()); | |
181 } | |
182 } else if line == b"[include]" { | |
183 if in_section && current == Current::Includes { | |
184 return Err(SparseConfigError::IncludesAfterExcludes { | |
185 context, | |
186 }); | |
187 } | |
188 in_section = true; | |
189 current = Current::Includes; | |
190 continue; | |
191 } else if line == b"[exclude]" { | |
192 in_section = true; | |
193 current = Current::Excludes; | |
194 } else { | |
195 if current == Current::None { | |
196 return Err(SparseConfigError::EntryOutsideSection { | |
197 context, | |
198 line: line.into(), | |
199 }); | |
200 } | |
201 if line.trim().starts_with(b"/") { | |
202 warnings.push(SparseWarning::RootWarning { | |
203 context, | |
204 line: line.into(), | |
205 }); | |
206 continue; | |
207 } | |
208 match current { | |
209 Current::Includes => { | |
210 includes.push(b'\n'); | |
211 includes.extend(line.iter()); | |
212 } | |
213 Current::Excludes => { | |
214 excludes.push(b'\n'); | |
215 excludes.extend(line.iter()); | |
216 } | |
217 Current::None => unreachable!(), | |
218 } | |
219 } | |
220 } | |
221 | |
222 Ok(SparseConfig { | |
223 includes, | |
224 excludes, | |
225 profiles, | |
226 warnings, | |
227 }) | |
228 } | |
229 | |
230 fn read_temporary_includes( | |
231 repo: &Repo, | |
232 ) -> Result<Vec<Vec<u8>>, SparseConfigError> { | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
233 let raw = repo.hg_vfs().try_read("tempsparse")?.unwrap_or_default(); |
49499 | 234 if raw.is_empty() { |
235 return Ok(vec![]); | |
236 } | |
237 Ok(raw.split(|c| *c == b'\n').map(ToOwned::to_owned).collect()) | |
238 } | |
239 | |
240 /// Obtain sparse checkout patterns for the given revision | |
241 fn patterns_for_rev( | |
242 repo: &Repo, | |
243 rev: Revision, | |
244 ) -> Result<Option<SparseConfig>, SparseConfigError> { | |
245 if !repo.has_sparse() { | |
246 return Ok(None); | |
247 } | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
248 let raw = repo.hg_vfs().try_read("sparse")?.unwrap_or_default(); |
49499 | 249 |
250 if raw.is_empty() { | |
251 return Ok(None); | |
252 } | |
253 | |
254 let mut config = parse_config(&raw, SparseConfigContext::Sparse)?; | |
255 | |
256 if !config.profiles.is_empty() { | |
257 let mut profiles: Vec<Vec<u8>> = config.profiles.into_iter().collect(); | |
258 let mut visited = HashSet::new(); | |
259 | |
260 while let Some(profile) = profiles.pop() { | |
261 if visited.contains(&profile) { | |
262 continue; | |
263 } | |
264 visited.insert(profile.to_owned()); | |
265 | |
266 let output = | |
267 cat(repo, &rev.to_string(), vec![HgPath::new(&profile)]) | |
268 .map_err(|_| { | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
269 HgError::corrupted( |
49499 | 270 "dirstate points to non-existent parent node" |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
271 .to_string(), |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
272 ) |
49499 | 273 })?; |
274 if output.results.is_empty() { | |
275 config.warnings.push(SparseWarning::ProfileNotFound { | |
276 profile: profile.to_owned(), | |
277 rev, | |
278 }) | |
279 } | |
280 | |
281 let subconfig = parse_config( | |
282 &output.results[0].1, | |
283 SparseConfigContext::Sparse, | |
284 )?; | |
285 if !subconfig.includes.is_empty() { | |
286 config.includes.push(b'\n'); | |
287 config.includes.extend(&subconfig.includes); | |
288 } | |
289 if !subconfig.includes.is_empty() { | |
290 config.includes.push(b'\n'); | |
291 config.excludes.extend(&subconfig.excludes); | |
292 } | |
293 config.warnings.extend(subconfig.warnings.into_iter()); | |
294 profiles.extend(subconfig.profiles.into_iter()); | |
295 } | |
296 | |
297 config.profiles = visited; | |
298 } | |
299 | |
300 if !config.includes.is_empty() { | |
301 config.includes.extend(b"\n.hg*"); | |
302 } | |
303 | |
304 Ok(Some(config)) | |
305 } | |
306 | |
307 /// Obtain a matcher for sparse working directories. | |
308 pub fn matcher( | |
309 repo: &Repo, | |
310 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> { | |
311 let mut warnings = vec![]; | |
312 if !repo.requirements().contains(SPARSE_REQUIREMENT) { | |
313 return Ok((Box::new(AlwaysMatcher), warnings)); | |
314 } | |
315 | |
316 let parents = repo.dirstate_parents()?; | |
317 let mut revs = vec![]; | |
318 let p1_rev = | |
319 repo.changelog()? | |
320 .rev_from_node(parents.p1.into()) | |
321 .map_err(|_| { | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
322 HgError::corrupted( |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
323 "dirstate points to non-existent parent node".to_string(), |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
324 ) |
49499 | 325 })?; |
326 if p1_rev != NULL_REVISION { | |
327 revs.push(p1_rev) | |
328 } | |
329 let p2_rev = | |
330 repo.changelog()? | |
331 .rev_from_node(parents.p2.into()) | |
332 .map_err(|_| { | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
333 HgError::corrupted( |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
334 "dirstate points to non-existent parent node".to_string(), |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
335 ) |
49499 | 336 })?; |
337 if p2_rev != NULL_REVISION { | |
338 revs.push(p2_rev) | |
339 } | |
340 let mut matchers = vec![]; | |
341 | |
342 for rev in revs.iter() { | |
343 let config = patterns_for_rev(repo, *rev); | |
344 if let Ok(Some(config)) = config { | |
345 warnings.extend(config.warnings); | |
346 let mut m: Box<dyn Matcher + Sync> = Box::new(AlwaysMatcher); | |
347 if !config.includes.is_empty() { | |
348 let (patterns, subwarnings) = parse_pattern_file_contents( | |
349 &config.includes, | |
350 Path::new(""), | |
50881
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
351 Some(PatternSyntax::Glob), |
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
352 false, |
49499 | 353 false, |
354 )?; | |
355 warnings.extend(subwarnings.into_iter().map(From::from)); | |
356 m = Box::new(IncludeMatcher::new(patterns)?); | |
357 } | |
358 if !config.excludes.is_empty() { | |
359 let (patterns, subwarnings) = parse_pattern_file_contents( | |
360 &config.excludes, | |
361 Path::new(""), | |
50881
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
362 Some(PatternSyntax::Glob), |
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
363 false, |
49499 | 364 false, |
365 )?; | |
366 warnings.extend(subwarnings.into_iter().map(From::from)); | |
367 m = Box::new(DifferenceMatcher::new( | |
368 m, | |
369 Box::new(IncludeMatcher::new(patterns)?), | |
370 )); | |
371 } | |
372 matchers.push(m); | |
373 } | |
374 } | |
375 let result: Box<dyn Matcher + Sync> = match matchers.len() { | |
376 0 => Box::new(AlwaysMatcher), | |
377 1 => matchers.pop().expect("1 is equal to 0"), | |
378 _ => Box::new(UnionMatcher::new(matchers)), | |
379 }; | |
380 | |
381 let matcher = | |
382 force_include_matcher(result, &read_temporary_includes(repo)?)?; | |
383 Ok((matcher, warnings)) | |
384 } | |
385 | |
386 /// Returns a matcher that returns true for any of the forced includes before | |
387 /// testing against the actual matcher | |
388 fn force_include_matcher( | |
389 result: Box<dyn Matcher + Sync>, | |
390 temp_includes: &[Vec<u8>], | |
391 ) -> Result<Box<dyn Matcher + Sync>, PatternError> { | |
392 if temp_includes.is_empty() { | |
393 return Ok(result); | |
394 } | |
395 let forced_include_matcher = IncludeMatcher::new( | |
396 temp_includes | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
397 .iter() |
49499 | 398 .map(|include| { |
399 IgnorePattern::new(PatternSyntax::Path, include, Path::new("")) | |
400 }) | |
401 .collect(), | |
402 )?; | |
403 Ok(Box::new(UnionMatcher::new(vec![ | |
404 Box::new(forced_include_matcher), | |
405 result, | |
406 ]))) | |
407 } |