Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/uv-warnings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ workspace = true
anstream = { workspace = true }
owo-colors = { workspace = true }
rustc-hash = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
indoc = { workspace = true }
insta = { workspace = true }
71 changes: 65 additions & 6 deletions crates/uv-warnings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ macro_rules! warn_user_once {
/// warning: Failed to create registry entry for Python 3.12
/// Caused By: Security policy forbids chaining registry entries
/// ```
///
/// ```text
/// error: Failed to download Python 3.12
/// Caused by: Failed to fetch https://example.com/upload/python3.13.tar.zst
/// Server says: This endpoint only support POST requests.
///
/// For downloads, please refer to https://example.com/download/python3.13.tar.zst
/// Caused by: Caused By: HTTP Error 400
/// ```
pub fn write_error_chain(
err: &dyn Error,
mut stream: impl std::fmt::Write,
Expand All @@ -88,12 +97,62 @@ pub fn write_error_chain(
err.to_string().trim()
)?;
for source in iter::successors(err.source(), |&err| err.source()) {
writeln!(
&mut stream,
" {}: {}",
"Caused by".color(color).bold(),
source.to_string().trim()
)?;
let msg = source.to_string();
let mut lines = msg.lines();
if let Some(first) = lines.next() {
let padding = " ";
let cause = "Caused by";
let child_padding = " ".repeat(padding.len() + cause.len() + 2);
writeln!(
&mut stream,
"{}{}: {}",
padding,
cause.color(color).bold(),
first.trim()
)?;
for line in lines {
let line = line.trim_end();
if line.is_empty() {
// Avoid showing indents on empty lines
writeln!(&mut stream)?;
} else {
writeln!(&mut stream, "{}{}", child_padding, line.trim_end())?;
}
}
}
}
Ok(())
}

#[cfg(test)]
mod tests {
use crate::write_error_chain;
use anyhow::anyhow;
use indoc::indoc;
use insta::assert_snapshot;
use owo_colors::AnsiColors;

#[test]
fn format_multiline_message() {
let err_middle = indoc! {"Failed to fetch https://example.com/upload/python3.13.tar.zst
Server says: This endpoint only support POST requests.

For downloads, please refer to https://example.com/download/python3.13.tar.zst"};
let err = anyhow!("Caused By: HTTP Error 400")
.context(err_middle)
.context("Failed to download Python 3.12");

let mut rendered = String::new();
write_error_chain(err.as_ref(), &mut rendered, "error", AnsiColors::Red).unwrap();
let rendered = anstream::adapter::strip_str(&rendered);

assert_snapshot!(rendered, @r"
error: Failed to download Python 3.12
Caused by: Failed to fetch https://example.com/upload/python3.13.tar.zst
Server says: This endpoint only support POST requests.

For downloads, please refer to https://example.com/download/python3.13.tar.zst
Caused by: Caused By: HTTP Error 400
");
}
}
Loading