1use crate::lines::Lines;
7use crate::problems::common::NoSpaceOnDevice;
8use crate::problems::debian::*;
9use crate::Problem;
10
11pub fn find_brz_build_error(lines: Vec<&str>) -> Option<(Option<Box<dyn Problem>>, String)> {
23 for (i, line) in lines.enumerate_backward(None) {
24 if let Some(suffix) = line.strip_prefix("brz: ERROR: ") {
25 let mut rest = vec![suffix.to_string()];
26 for n in lines[i + 1..].iter() {
27 if n.starts_with(" ") {
28 rest.push(n.to_string());
29 }
30 }
31 let reflowed = rest.join("\n");
32 let (err, line) = parse_brz_error(&reflowed, lines[..i].to_vec());
33 return Some((err, line.to_string()));
34 }
35 }
36 None
37}
38
39fn parse_debcargo_failure(_: ®ex::Captures, prior_lines: Vec<&str>) -> Option<Box<dyn Problem>> {
51 const MORE_TAIL: &str = "\x1b[0m\n";
52 const MORE_HEAD1: &str = "\x1b[1;31mSomething failed: ";
53 const MORE_HEAD2: &str = "\x1b[1;31mdebcargo failed: ";
54 if let Some(extra) = prior_lines.last().unwrap().strip_suffix(MORE_TAIL) {
55 let mut extra = vec![extra];
56 for line in prior_lines[..prior_lines.len() - 1].iter().rev() {
57 if let Some(middle) = extra[0].strip_prefix(MORE_HEAD1) {
58 extra[0] = middle;
59 break;
60 }
61 if let Some(middle) = extra[0].strip_prefix(MORE_HEAD2) {
62 extra[0] = middle;
63 break;
64 }
65 extra.insert(0, line);
66 }
67 if extra.len() == 1 {
68 extra = vec![];
69 }
70 if extra
71 .last()
72 .and_then(|l| l.strip_prefix("Try `debcargo update` to update the crates.io index."))
73 .is_some()
74 {
75 if let Some((_, n)) = lazy_regex::regex_captures!(
76 r"Couldn't find any crate matching (.*)",
77 extra[extra.len() - 2].trim_end()
78 ) {
79 return Some(Box::new(MissingDebcargoCrate::from_string(n)));
80 } else {
81 return Some(Box::new(DpkgSourcePackFailed(
82 extra[extra.len() - 2].to_owned(),
83 )));
84 }
85 } else if !extra.is_empty() {
86 if let Some((_, d, p)) = lazy_regex::regex_captures!(
87 r"Cannot represent prerelease part of dependency: (.*) Predicate \{ (.*) \}",
88 extra[0]
89 ) {
90 return Some(Box::new(DebcargoUnacceptablePredicate {
91 cratename: d.to_owned(),
92 predicate: p.to_owned(),
93 }));
94 } else if let Some((_, d, c)) = lazy_regex::regex_captures!(
95 r"Cannot represent prerelease part of dependency: (.*) Comparator \{ (.*) \}",
96 extra[0]
97 ) {
98 return Some(Box::new(DebcargoUnacceptableComparator {
99 cratename: d.to_owned(),
100 comparator: c.to_owned(),
101 }));
102 }
103 } else {
104 return Some(Box::new(DebcargoFailure(extra.join(""))));
105 }
106 }
107
108 Some(Box::new(DebcargoFailure(
109 "Debcargo failed to run".to_string(),
110 )))
111}
112
113macro_rules! regex_line_matcher {
114 ($re:expr, $f:expr) => {
115 (regex::Regex::new($re).unwrap(), $f)
116 };
117}
118
119lazy_static::lazy_static! {
120 static ref BRZ_ERRORS: Vec<(regex::Regex, fn(®ex::Captures, Vec<&str>) -> Option<Box<dyn Problem>>)> = vec![
121 regex_line_matcher!("Unable to find the needed upstream tarball for package (.*), version (.*)\\.",
122 |m, _| Some(Box::new(UnableToFindUpstreamTarball{package: m.get(1).unwrap().as_str().to_string(), version: m.get(2).unwrap().as_str().parse().unwrap()}))),
123 regex_line_matcher!("Unknown mercurial extra fields in (.*): b'(.*)'.", |m, _| Some(Box::new(UnknownMercurialExtraFields(m.get(2).unwrap().as_str().to_string())))),
124 regex_line_matcher!("UScan failed to run: In watchfile (.*), reading webpage (.*) failed: 429 too many requests\\.", |m, _| Some(Box::new(UScanTooManyRequests(m.get(2).unwrap().as_str().to_string())))),
125 regex_line_matcher!("UScan failed to run: OpenPGP signature did not verify..", |_, _| Some(Box::new(UpstreamPGPSignatureVerificationFailed))),
126 regex_line_matcher!(r"Inconsistency between source format and version: version is( not)? native, format is( not)? native\.", |m, _| Some(Box::new(InconsistentSourceFormat{version: m.get(1).is_some(), source_format: m.get(2).is_some()}))),
127 regex_line_matcher!(r"UScan failed to run: In (.*) no matching hrefs for version (.*) in watch line", |m, _| Some(Box::new(UScanRequestVersionMissing(m.get(2).unwrap().as_str().to_string())))),
128 regex_line_matcher!(r"UScan failed to run: In directory ., downloading (.*) failed: (.*)", |m, _| Some(Box::new(UScanFailed{url: m.get(1).unwrap().as_str().to_string(), reason: m.get(2).unwrap().as_str().to_string()}))),
129 regex_line_matcher!(r"UScan failed to run: In watchfile debian/watch, reading webpage\n (.*) failed: (.*)", |m, _| Some(Box::new(UScanFailed{url: m.get(1).unwrap().as_str().to_string(), reason: m.get(2).unwrap().as_str().to_string()}))),
130 regex_line_matcher!(r"Unable to parse upstream metadata file (.*): (.*)", |m, _| Some(Box::new(UpstreamMetadataFileParseError{path: m.get(1).unwrap().as_str().to_string().into(), reason: m.get(2).unwrap().as_str().to_string()}))),
131 regex_line_matcher!(r"Debcargo failed to run\.", parse_debcargo_failure),
132 regex_line_matcher!(r"\[Errno 28\] No space left on device", |_, _| Some(Box::new(NoSpaceOnDevice)))
133 ];
134}
135
136pub fn parse_brz_error<'a>(
150 line: &'a str,
151 prior_lines: Vec<&'a str>,
152) -> (Option<Box<dyn Problem>>, String) {
153 let line = line.trim();
154 for (re, f) in BRZ_ERRORS.iter() {
155 if let Some(m) = re.captures(line) {
156 let err = f(&m, prior_lines);
157 let description = err.as_ref().unwrap().to_string();
158 return (err, description);
159 }
160 }
161 if let Some(suffix) = line.strip_prefix("UScan failed to run: ") {
162 return (
163 Some(Box::new(UScanError(suffix.to_owned()))),
164 line.to_string(),
165 );
166 }
167 if let Some(suffix) = line.strip_prefix("Unable to parse changelog: ") {
168 return (
169 Some(Box::new(ChangelogParseError(
170 suffix.to_string().to_string(),
171 ))),
172 line.to_string(),
173 );
174 }
175 return (None, line.split_once('\n').unwrap().0.to_string());
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 #[test]
182 fn test_inconsistent_source_format() {
183 let (err, line) = parse_brz_error(
184 "Inconsistency between source format and version: version is not native, format is native.",
185 vec![]);
186 assert_eq!(
187 line,
188 "Inconsistent source format between version and source format",
189 );
190 assert_eq!(
191 Some(Box::new(InconsistentSourceFormat {
192 version: true,
193 source_format: false
194 }) as Box<dyn Problem>),
195 err
196 );
197 }
198
199 #[test]
200 fn test_missing_debcargo_crate() {
201 let lines = vec![
202 "Using crate name: version-check, version 0.9.2 Updating crates.io index\n",
203 "\x1b[1;31mSomething failed: Couldn't find any crate matching version-check = 0.9.2\n",
204 "Try `debcargo update` to update the crates.io index.\x1b[0m\n",
205 "brz: ERROR: Debcargo failed to run.\n",
206 ];
207 let (err, line) = find_brz_build_error(lines).unwrap();
208 assert_eq!(
209 line,
210 "debcargo can't find crate version-check (version: 0.9.2)"
211 );
212 assert_eq!(
213 err,
214 Some(Box::new(MissingDebcargoCrate {
215 cratename: "version-check".to_string(),
216 version: Some("0.9.2".to_string())
217 }) as Box<dyn Problem>)
218 );
219 }
220
221 #[test]
222 fn test_missing_debcargo_crate2() {
223 let lines = vec![
224 "Running 'sbuild -A -s -v'\n",
225 "Building using working tree\n",
226 "Building package in merge mode\n",
227 "Using crate name: utf8parse, version 0.10.1+git20220116.1.dfac57e\n",
228 " Updating crates.io index\n",
229 " Updating crates.io index\n",
230 "\x1b[1;31mdebcargo failed: Couldn't find any crate matching utf8parse =0.10.1\n",
231 "Try `debcargo update` to update the crates.io index.\x1b[0m\n",
232 "brz: ERROR: Debcargo failed to run.\n",
233 ];
234 let (err, line) = find_brz_build_error(lines).unwrap();
235 assert_eq!(
236 line,
237 "debcargo can't find crate utf8parse (version: 0.10.1)"
238 );
239 assert_eq!(
240 err,
241 Some(Box::new(MissingDebcargoCrate {
242 cratename: "utf8parse".to_owned(),
243 version: Some("0.10.1".to_owned())
244 }) as Box<dyn Problem>)
245 );
246 }
247}