#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GleamFailureLayer {
DependencyLayer,
NotDependencyLayer,
}
const REGISTRY_FAILURE_TITLES: &[&str] = &[
"Failed to download package",
"Failure opening tar archive",
"Hex API failure",
"HTTP error",
];
const RESOLUTION_FAILURE_TITLE: &str = "Dependency resolution failed";
const RESOLUTION_TRANSPORT_MARKERS: &[&str] = &["error sending request", "repo.hex.pm"];
#[must_use]
pub fn classify_gleam_failure(combined: &str) -> GleamFailureLayer {
for line in combined.lines() {
let Some(title) = line.trim_start().strip_prefix("error: ") else {
continue;
};
let title = title.trim();
if REGISTRY_FAILURE_TITLES.contains(&title) {
return GleamFailureLayer::DependencyLayer;
}
if title == RESOLUTION_FAILURE_TITLE
&& RESOLUTION_TRANSPORT_MARKERS
.iter()
.any(|marker| combined.contains(marker))
{
return GleamFailureLayer::DependencyLayer;
}
}
GleamFailureLayer::NotDependencyLayer
}
#[cfg(test)]
mod tests {
use super::{GleamFailureLayer, classify_gleam_failure};
const HEX_OUTAGE: &str = " Resolving versions\nerror: HTTP error\n\nA HTTP request failed.\n\
The error from the HTTP client was:\n\n error sending request for url \
(https://hex.pm/api/packages/gleam_stdlib/releases/1.0.3)\n";
#[test]
fn the_hex_outage_that_opened_this_task_is_a_dependency_layer_failure() {
assert_eq!(
classify_gleam_failure(HEX_OUTAGE),
GleamFailureLayer::DependencyLayer
);
}
#[test]
fn a_tar_archive_failure_is_a_dependency_layer_failure() {
assert_eq!(
classify_gleam_failure("error: Failure opening tar archive\n"),
GleamFailureLayer::DependencyLayer
);
}
#[test]
fn a_type_error_is_not_a_dependency_layer_failure() {
let output =
"error: Type mismatch\n\nExpected type:\n\n Int\n\nFound type:\n\n String\n";
assert_eq!(
classify_gleam_failure(output),
GleamFailureLayer::NotDependencyLayer
);
}
#[test]
fn an_unsatisfiable_requirement_is_not_excused_as_infrastructure() {
let output = "error: Dependency resolution failed\n\nAn error occurred while choosing \
the version of gleam_stdlib: no compatible version exists\n";
assert_eq!(
classify_gleam_failure(output),
GleamFailureLayer::NotDependencyLayer
);
}
#[test]
fn a_resolution_failure_that_names_a_transport_fault_is_infrastructure() {
let output = "error: Dependency resolution failed\n\nAn error occurred while choosing \
the version of gleam_json: error sending request for url \
(https://repo.hex.pm/packages/gleam_json)\n";
assert_eq!(
classify_gleam_failure(output),
GleamFailureLayer::DependencyLayer
);
}
#[test]
fn a_source_snippet_quoting_a_registry_title_does_not_decide_the_classification() {
let output = "error: Syntax error\n\n 3 │ let message = \"HTTP error\"\n │\
\n ╵ I was expecting a value here\n";
assert_eq!(
classify_gleam_failure(output),
GleamFailureLayer::NotDependencyLayer,
"a registry title inside a QUOTED SOURCE LINE must not classify the failure"
);
}
#[test]
fn download_progress_alone_is_not_a_dependency_layer_failure() {
let output = " Downloading packages\n Downloaded 2 packages in 0.11s\n\
error: Type mismatch\n";
assert_eq!(
classify_gleam_failure(output),
GleamFailureLayer::NotDependencyLayer
);
}
#[test]
fn empty_output_is_not_a_dependency_layer_failure() {
assert_eq!(
classify_gleam_failure(""),
GleamFailureLayer::NotDependencyLayer
);
}
}