Mercurial > public > mercurial-scm > hg-stable
annotate rust/hg-core/src/sparse.rs @ 53040:cdd7bf612c7b stable tip
bundle-spec: properly format boolean parameter (issue6960)
This was breaking automatic clone bundle generation. This changeset fixes it and
add a test to catch it in the future.
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Tue, 11 Mar 2025 02:29:42 +0100 |
parents | 42f78c859dd1 |
children |
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, | |
52774
94e2547e6f3d
rust: move code from utils to utils::strings
Mitchell Kember <mkember@janestreet.com>
parents:
52339
diff
changeset
|
20 utils::{hg_path::HgPath, strings::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>), |
52267
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
92 /// 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
|
93 /// 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
|
94 /// the config file. |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
95 WhitespaceAtEdgeOfPattern(Vec<u8>), |
49499 | 96 #[from] |
97 HgError(HgError), | |
98 #[from] | |
99 PatternError(PatternError), | |
100 } | |
101 | |
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
102 impl From<SparseConfigError> for HgError { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
103 fn from(value: SparseConfigError) -> Self { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
104 match value { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
105 SparseConfigError::IncludesAfterExcludes { context } => { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
106 HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
107 message: format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
108 "{} config cannot have includes after excludes", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
109 context, |
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 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
112 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
113 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
114 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
115 SparseConfigError::EntryOutsideSection { context, line } => { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
116 HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
117 message: format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
118 "{} config entry outside of section: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
119 context, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
120 String::from_utf8_lossy(&line) |
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 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
123 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
124 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
125 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
126 SparseConfigError::IncludesInNarrow => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
127 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
|
128 supported in narrowspec" |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
129 .to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
130 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
131 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
132 }, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
133 SparseConfigError::InvalidNarrowPrefix(vec) => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
134 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
|
135 b"invalid prefix on narrow pattern: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
136 vec |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
137 )) |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
138 .to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
139 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
140 hint: Some(format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
141 "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
|
142 VALID_PREFIXES.join(", ") |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
143 )), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
144 }, |
52267
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
145 SparseConfigError::WhitespaceAtEdgeOfPattern(vec) => { |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
146 HgError::Abort { |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
147 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
|
148 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
|
149 vec |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
150 )) |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
151 .to_string(), |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
152 detailed_exit_code: STATE_ERROR, |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
153 hint: Some( |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
154 "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
|
155 .to_string(), |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
156 ), |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
157 } |
77b95a4abbb2
narrow: stricter validation of narrowspec patterns in rhg
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
52067
diff
changeset
|
158 } |
52067
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
159 SparseConfigError::HgError(hg_error) => hg_error, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
160 SparseConfigError::PatternError(pattern_error) => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
161 message: pattern_error.to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
162 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
163 hint: None, |
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 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
167 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50881
diff
changeset
|
168 |
49499 | 169 /// 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
|
170 pub(crate) fn parse_config( |
49499 | 171 raw: &[u8], |
172 context: SparseConfigContext, | |
173 ) -> Result<SparseConfig, SparseConfigError> { | |
174 let mut includes = vec![]; | |
175 let mut excludes = vec![]; | |
176 let mut profiles = HashSet::new(); | |
177 let mut warnings = vec![]; | |
178 | |
179 #[derive(PartialEq, Eq)] | |
180 enum Current { | |
181 Includes, | |
182 Excludes, | |
183 None, | |
49522
52464a20add0
rhg: parallellize computation of [unsure_is_modified]
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49502
diff
changeset
|
184 } |
49499 | 185 |
186 let mut current = Current::None; | |
187 let mut in_section = false; | |
188 | |
189 for line in raw.split(|c| *c == b'\n') { | |
190 let line = line.trim(); | |
191 if line.is_empty() || line[0] == b'#' { | |
192 // empty or comment line, skip | |
193 continue; | |
194 } | |
195 if line.starts_with(b"%include ") { | |
196 let profile = line[b"%include ".len()..].trim(); | |
197 if !profile.is_empty() { | |
198 profiles.insert(profile.into()); | |
199 } | |
200 } else if line == b"[include]" { | |
201 if in_section && current == Current::Includes { | |
202 return Err(SparseConfigError::IncludesAfterExcludes { | |
203 context, | |
204 }); | |
205 } | |
206 in_section = true; | |
207 current = Current::Includes; | |
208 continue; | |
209 } else if line == b"[exclude]" { | |
210 in_section = true; | |
211 current = Current::Excludes; | |
212 } else { | |
213 if current == Current::None { | |
214 return Err(SparseConfigError::EntryOutsideSection { | |
215 context, | |
216 line: line.into(), | |
217 }); | |
218 } | |
219 if line.trim().starts_with(b"/") { | |
220 warnings.push(SparseWarning::RootWarning { | |
221 context, | |
222 line: line.into(), | |
223 }); | |
224 continue; | |
225 } | |
226 match current { | |
227 Current::Includes => { | |
228 includes.push(b'\n'); | |
229 includes.extend(line.iter()); | |
230 } | |
231 Current::Excludes => { | |
232 excludes.push(b'\n'); | |
233 excludes.extend(line.iter()); | |
234 } | |
235 Current::None => unreachable!(), | |
236 } | |
237 } | |
238 } | |
239 | |
240 Ok(SparseConfig { | |
241 includes, | |
242 excludes, | |
243 profiles, | |
244 warnings, | |
245 }) | |
246 } | |
247 | |
248 fn read_temporary_includes( | |
249 repo: &Repo, | |
250 ) -> 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
|
251 let raw = repo.hg_vfs().try_read("tempsparse")?.unwrap_or_default(); |
49499 | 252 if raw.is_empty() { |
253 return Ok(vec![]); | |
254 } | |
255 Ok(raw.split(|c| *c == b'\n').map(ToOwned::to_owned).collect()) | |
256 } | |
257 | |
258 /// Obtain sparse checkout patterns for the given revision | |
259 fn patterns_for_rev( | |
260 repo: &Repo, | |
261 rev: Revision, | |
262 ) -> Result<Option<SparseConfig>, SparseConfigError> { | |
263 if !repo.has_sparse() { | |
264 return Ok(None); | |
265 } | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
266 let raw = repo.hg_vfs().try_read("sparse")?.unwrap_or_default(); |
49499 | 267 |
268 if raw.is_empty() { | |
269 return Ok(None); | |
270 } | |
271 | |
272 let mut config = parse_config(&raw, SparseConfigContext::Sparse)?; | |
273 | |
274 if !config.profiles.is_empty() { | |
275 let mut profiles: Vec<Vec<u8>> = config.profiles.into_iter().collect(); | |
276 let mut visited = HashSet::new(); | |
277 | |
278 while let Some(profile) = profiles.pop() { | |
279 if visited.contains(&profile) { | |
280 continue; | |
281 } | |
282 visited.insert(profile.to_owned()); | |
283 | |
284 let output = | |
285 cat(repo, &rev.to_string(), vec![HgPath::new(&profile)]) | |
286 .map_err(|_| { | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
287 HgError::corrupted( |
49499 | 288 "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
|
289 .to_string(), |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
290 ) |
49499 | 291 })?; |
292 if output.results.is_empty() { | |
293 config.warnings.push(SparseWarning::ProfileNotFound { | |
294 profile: profile.to_owned(), | |
295 rev, | |
296 }) | |
297 } | |
298 | |
299 let subconfig = parse_config( | |
300 &output.results[0].1, | |
301 SparseConfigContext::Sparse, | |
302 )?; | |
303 if !subconfig.includes.is_empty() { | |
304 config.includes.push(b'\n'); | |
305 config.includes.extend(&subconfig.includes); | |
306 } | |
307 if !subconfig.includes.is_empty() { | |
308 config.includes.push(b'\n'); | |
309 config.excludes.extend(&subconfig.excludes); | |
310 } | |
311 config.warnings.extend(subconfig.warnings.into_iter()); | |
312 profiles.extend(subconfig.profiles.into_iter()); | |
313 } | |
314 | |
315 config.profiles = visited; | |
316 } | |
317 | |
318 if !config.includes.is_empty() { | |
319 config.includes.extend(b"\n.hg*"); | |
320 } | |
321 | |
322 Ok(Some(config)) | |
323 } | |
324 | |
325 /// Obtain a matcher for sparse working directories. | |
326 pub fn matcher( | |
327 repo: &Repo, | |
328 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> { | |
329 let mut warnings = vec![]; | |
330 if !repo.requirements().contains(SPARSE_REQUIREMENT) { | |
331 return Ok((Box::new(AlwaysMatcher), warnings)); | |
332 } | |
333 | |
334 let parents = repo.dirstate_parents()?; | |
335 let mut revs = vec![]; | |
336 let p1_rev = | |
337 repo.changelog()? | |
338 .rev_from_node(parents.p1.into()) | |
339 .map_err(|_| { | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
340 HgError::corrupted( |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
341 "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
|
342 ) |
49499 | 343 })?; |
344 if p1_rev != NULL_REVISION { | |
345 revs.push(p1_rev) | |
346 } | |
347 let p2_rev = | |
348 repo.changelog()? | |
349 .rev_from_node(parents.p2.into()) | |
350 .map_err(|_| { | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
351 HgError::corrupted( |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
352 "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
|
353 ) |
49499 | 354 })?; |
355 if p2_rev != NULL_REVISION { | |
356 revs.push(p2_rev) | |
357 } | |
358 let mut matchers = vec![]; | |
359 | |
360 for rev in revs.iter() { | |
361 let config = patterns_for_rev(repo, *rev); | |
362 if let Ok(Some(config)) = config { | |
363 warnings.extend(config.warnings); | |
364 let mut m: Box<dyn Matcher + Sync> = Box::new(AlwaysMatcher); | |
365 if !config.includes.is_empty() { | |
366 let (patterns, subwarnings) = parse_pattern_file_contents( | |
367 &config.includes, | |
368 Path::new(""), | |
50881
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
369 Some(PatternSyntax::Glob), |
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
370 false, |
49499 | 371 false, |
372 )?; | |
373 warnings.extend(subwarnings.into_iter().map(From::from)); | |
374 m = Box::new(IncludeMatcher::new(patterns)?); | |
375 } | |
376 if !config.excludes.is_empty() { | |
377 let (patterns, subwarnings) = parse_pattern_file_contents( | |
378 &config.excludes, | |
379 Path::new(""), | |
50881
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
380 Some(PatternSyntax::Glob), |
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50768
diff
changeset
|
381 false, |
49499 | 382 false, |
383 )?; | |
384 warnings.extend(subwarnings.into_iter().map(From::from)); | |
385 m = Box::new(DifferenceMatcher::new( | |
386 m, | |
387 Box::new(IncludeMatcher::new(patterns)?), | |
388 )); | |
389 } | |
390 matchers.push(m); | |
391 } | |
392 } | |
393 let result: Box<dyn Matcher + Sync> = match matchers.len() { | |
394 0 => Box::new(AlwaysMatcher), | |
395 1 => matchers.pop().expect("1 is equal to 0"), | |
396 _ => Box::new(UnionMatcher::new(matchers)), | |
397 }; | |
398 | |
399 let matcher = | |
400 force_include_matcher(result, &read_temporary_includes(repo)?)?; | |
401 Ok((matcher, warnings)) | |
402 } | |
403 | |
404 /// Returns a matcher that returns true for any of the forced includes before | |
405 /// testing against the actual matcher | |
406 fn force_include_matcher( | |
407 result: Box<dyn Matcher + Sync>, | |
408 temp_includes: &[Vec<u8>], | |
409 ) -> Result<Box<dyn Matcher + Sync>, PatternError> { | |
410 if temp_includes.is_empty() { | |
411 return Ok(result); | |
412 } | |
413 let forced_include_matcher = IncludeMatcher::new( | |
414 temp_includes | |
50003
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49522
diff
changeset
|
415 .iter() |
49499 | 416 .map(|include| { |
417 IgnorePattern::new(PatternSyntax::Path, include, Path::new("")) | |
418 }) | |
419 .collect(), | |
420 )?; | |
421 Ok(Box::new(UnionMatcher::new(vec![ | |
422 Box::new(forced_include_matcher), | |
423 result, | |
424 ]))) | |
425 } |