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