From 5d363cc3cdf6e2f591161cc7152f4d06f8b0a477 Mon Sep 17 00:00:00 2001 From: konsti Date: Mon, 28 Oct 2024 14:02:49 +0100 Subject: [PATCH] Improve serde error output (#982) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In our tests using the serde impl on `Url`, we found that failing URL serialization says the inverse of what it should say. ```toml [project] name = "foo" version = "0.0.0" dependencies = [ "tqdm ==4.66.0", ] [tool.uv.sources] tqdm = { url = "§invalid#+#*Ä" } ``` ``` error: Failed to parse: `pyproject.toml` Caused by: TOML parse error at line 10, column 16 | 10 | tqdm = { url = "§invalid#+#*Ä" } | ^^^^^^^^^^^^^^^^^ invalid value: string "§invalid#+#*Ä", expected relative URL without a base ``` It says that expected a relative URL without a base, when this was the unexpected input that caused the error. Using `serde::de::Error::custom` oder `serde::de::Error::invalid_value` improves the error message: ``` error: TOML parse error at line 8, column 16 | 8 | tqdm = { url = "§invalid#+#*Ä" } | ^^^^^^^^^^^^^^^^^ relative URL without a base: "§invalid#+#*Ä" ``` --- url/src/lib.rs | 15 +++++---------- url/tests/unit.rs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/url/src/lib.rs b/url/src/lib.rs index c40a9ea3..cc263a79 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -2700,7 +2700,7 @@ impl Url { where D: serde::Deserializer<'de>, { - use serde::de::{Deserialize, Error, Unexpected}; + use serde::de::{Deserialize, Error}; let ( serialization, scheme_end, @@ -2726,10 +2726,8 @@ impl Url { fragment_start, }; if cfg!(debug_assertions) { - url.check_invariants().map_err(|reason| { - let reason: &str = &reason; - Error::invalid_value(Unexpected::Other("value"), &reason) - })? + url.check_invariants() + .map_err(|reason| Error::custom(reason))? } Ok(url) } @@ -2942,7 +2940,7 @@ impl<'de> serde::Deserialize<'de> for Url { where D: serde::Deserializer<'de>, { - use serde::de::{Error, Unexpected, Visitor}; + use serde::de::{Error, Visitor}; struct UrlVisitor; @@ -2957,10 +2955,7 @@ impl<'de> serde::Deserialize<'de> for Url { where E: Error, { - Url::parse(s).map_err(|err| { - let err_s = format!("{}", err); - Error::invalid_value(Unexpected::Str(s), &err_s.as_str()) - }) + Url::parse(s).map_err(|err| Error::custom(format!("{}: {:?}", err, s))) } } diff --git a/url/tests/unit.rs b/url/tests/unit.rs index c2cd3ef3..b3596610 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -1362,3 +1362,20 @@ fn issue_974() { let _ = url::quirks::set_port(&mut url, "\u{0000}9000"); assert_eq!(url.port(), Some(8000)); } + +#[cfg(feature = "serde")] +#[test] +fn serde_error_message() { + use serde::Deserialize; + #[derive(Debug, Deserialize)] + #[allow(dead_code)] + struct TypeWithUrl { + url: Url, + } + + let err = serde_json::from_str::(r#"{"url": "§invalid#+#*Ä"}"#).unwrap_err(); + assert_eq!( + err.to_string(), + r#"relative URL without a base: "§invalid#+#*Ä" at line 1 column 25"# + ); +}