From 1b19687d253364ccabc4dab43a9f29a2f6cfe1a8 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Mon, 23 Sep 2024 15:34:01 -0300 Subject: [PATCH 01/12] fix: unicode on exactly index 7 should not panic --- src/de.rs | 2 +- src/lib.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index fd7e84e..72aa340 100644 --- a/src/de.rs +++ b/src/de.rs @@ -25,7 +25,7 @@ impl<'a, T: DeserializeParams<'a>> Uri<'a, bitcoin::address::NetworkUnchecked, T return Err(Error::Uri(UriError(UriErrorInner::TooShort))); } - if !string[..SCHEME.len()].eq_ignore_ascii_case(SCHEME) { + if !string.get(..SCHEME.len()).is_some_and(|s| s.eq_ignore_ascii_case(SCHEME)) { return Err(Error::Uri(UriError(UriErrorInner::InvalidScheme))); } diff --git a/src/lib.rs b/src/lib.rs index b149115..73376c6 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -427,4 +427,11 @@ mod tests { assert!(uri.label.is_none()); assert!(uri.message.is_none()); } + + #[test] + fn bad_unicode_scheme() { + let input = "bitcoinö:1andreas3batLhQa2FawWjeyjCqyBzypd"; + let uri = input.parse::>(); + assert!(uri.is_err()); + } } From c38295c907b91edf2b45e6b85aacf007e32dd6ca Mon Sep 17 00:00:00 2001 From: Yuval Kogman Date: Mon, 21 Oct 2024 20:54:00 +0200 Subject: [PATCH 02/12] test that unknown parameters aren't retained --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b149115..fde599d 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -426,5 +426,7 @@ mod tests { assert!(uri.amount.is_none()); assert!(uri.label.is_none()); assert!(uri.message.is_none()); + + assert_eq!(uri.to_string(), "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd"); } } From eb326385b8f3d44ffd1b5b4e38c16a1daca7fe4e Mon Sep 17 00:00:00 2001 From: Yuval Kogman Date: Mon, 21 Oct 2024 21:08:14 +0200 Subject: [PATCH 03/12] test escaping in parameters --- src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index fde599d..5867071 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -429,4 +429,17 @@ mod tests { assert_eq!(uri.to_string(), "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd"); } + + #[test] + fn label_with_rfc3986_param_separator() { + let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo%26bar%20%3D%20baz%3F"; + let uri = input.parse::>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap(); + let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap(); + assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd"); + assert_eq!(label, "foo&bar = baz?"); + assert!(uri.amount.is_none()); + assert!(uri.message.is_none()); + + assert_eq!(uri.to_string(), input); + } } From 058580680964618c486174d5ed2fdddb9890b267 Mon Sep 17 00:00:00 2001 From: Yuval Kogman Date: Mon, 21 Oct 2024 21:10:36 +0200 Subject: [PATCH 04/12] Specify escaped characters more conservatively MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit '#'' is not in the set qchar indirectly defined in BIP 21, and therefore should be escaped. [BIP 21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki#abnf-grammar): > labelparam = "label=" *qchar > messageparam = "message=" *qchar > otherparam = qchar *qchar [ "=" *qchar ] ... > Here, "qchar" corresponds to valid characters of an RFC 3986 URI query > component, excluding the "=" and "&" characters, which this BIP takes > as separators. [RFC 3986 § 3.4](https://www.rfc-editor.org/rfc/rfc3986#section-3.4): > The query component is indicated by the first question mark ("?") > character and terminated by a number sign ("#") character or by the > end of the URI. [RFC 3986 Appendix A](https://www.rfc-editor.org/rfc/rfc3986#appendix-A): > pchar = unreserved / pct-encoded / sub-delims / ":" / "@" > query = *( pchar / "/" / "?" ) ... > pct-encoded = "%" HEXDIG HEXDIG > unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" ... > sub-delims = "!" / "$" / "&" / "'" / "(" / ")" > / "*" / "+" / "," / ";" / "=" --- src/lib.rs | 17 +++++++++++++++-- src/ser.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5867071..871d543 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -432,11 +432,24 @@ mod tests { #[test] fn label_with_rfc3986_param_separator() { - let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo%26bar%20%3D%20baz%3F"; + let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo%26bar%20%3D%20baz/blah?;:@"; let uri = input.parse::>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap(); let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap(); assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd"); - assert_eq!(label, "foo&bar = baz?"); + assert_eq!(label, "foo&bar = baz/blah?;:@"); + assert!(uri.amount.is_none()); + assert!(uri.message.is_none()); + + assert_eq!(uri.to_string(), input); + } + + #[test] + fn label_with_rfc3986_fragment_separator() { + let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo%23bar"; + let uri = input.parse::>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap(); + let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap(); + assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd"); + assert_eq!(label, "foo#bar"); assert!(uri.amount.is_none()); assert!(uri.message.is_none()); diff --git a/src/ser.rs b/src/ser.rs index 997a511..678b669 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -47,7 +47,61 @@ impl<'a, W: fmt::Write> fmt::Write for EqSignChecker<'a, W> { } /// Set of characters that will be percent-encoded -const ASCII_SET: percent_encoding_rfc3986::AsciiSet = percent_encoding_rfc3986::CONTROLS.add(b'&').add(b'?').add(b' ').add(b'='); +/// +/// This contains anything not in `query` (i.e. ``gen-delim` from the quoted +/// definitions`) as per RFC 3986, as well as '&' and '=' as per BIP 21. +/// +/// [BIP 21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki#abnf-grammar): +/// +/// > ```text +/// > labelparam = "label=" *qchar +/// > messageparam = "message=" *qchar +/// > otherparam = qchar *qchar [ "=" *qchar ] +/// > ``` +/// ... +/// > Here, "qchar" corresponds to valid characters of an RFC 3986 URI > query +/// component, excluding the "=" and "&" characters, which this BIP > takes as +/// separators. +/// +/// [RFC 3986 Appendix A](https://www.rfc-editor.org/rfc/rfc3986#appendix-A): +/// +/// > ```text +/// > pchar = unreserved / pct-encoded / sub-delims / ":" / "@" +/// > query = *( pchar / "/" / "?" ) +/// > ``` +/// ... +/// > ```text +/// > pct-encoded = "%" HEXDIG HEXDIG +/// > unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" +/// > ``` +/// ... +/// > ```text +/// > sub-delims = "!" / "$" / "&" / "'" / "(" / ")" +/// > / "*" / "+" / "," / ";" / "=" +/// > ``` +const ASCII_SET: percent_encoding_rfc3986::AsciiSet = percent_encoding_rfc3986::NON_ALPHANUMERIC + // allow non-alphanumeric characters from `unreserved` + .remove(b'-') + .remove(b'.') + .remove(b'_') + .remove(b'~') + // allow non-alphanumeric characters from `sub-delims` excluding bip-21 + // separators ("&", and "=") + .remove(b'!') + .remove(b'$') + .remove(b'\'') + .remove(b'(') + .remove(b')') + .remove(b'*') + .remove(b'+') + .remove(b',') + .remove(b';') + // allow pchar extra chars + .remove(b':') + .remove(b'@') + // allow query extra chars + .remove(b'/') + .remove(b'?'); /// Percent-encodes writes. struct WriterEncoder(W); From 646de258958d37b4b4878b23532de8eb81f0c110 Mon Sep 17 00:00:00 2001 From: Yuval Kogman Date: Mon, 21 Oct 2024 21:14:12 +0200 Subject: [PATCH 05/12] Ignore RFC 3986 fragments in BIP 21 URIs Although behavior for when encountering RFC 3986 fragments in BIP 21 URIs is not specified, according to RFC 3986 it is unambiguously not query data and therefore should be excluded from BIP 21 query parameters. --- src/de.rs | 10 ++++++++++ src/lib.rs | 24 ++++++++++++++++++++++++ src/ser.rs | 6 +++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/de.rs b/src/de.rs index fd7e84e..7080130 100644 --- a/src/de.rs +++ b/src/de.rs @@ -42,6 +42,16 @@ impl<'a, T: DeserializeParams<'a>> Uri<'a, bitcoin::address::NetworkUnchecked, T let mut label = None; let mut message = None; if let Some(params) = params { + // [RFC 3986 § 3.4](https://www.rfc-editor.org/rfc/rfc3986#section-3.4): + // + // > The query component is indicated by the first question + // > mark ("?") character and terminated by a number sign ("#") character + // > or by the end of the URI. + let params = match params.find('#') { + Some(pos) => ¶ms[..pos], + None => params, + }; + for param in params.split('&') { let pos = param .find('=') diff --git a/src/lib.rs b/src/lib.rs index 871d543..5f699ff 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -455,4 +455,28 @@ mod tests { assert_eq!(uri.to_string(), input); } + + #[test] + fn rfc3986_empty_fragment_not_defined_in_bip21() { + let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo#"; + let uri = input.parse::>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap(); + let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap(); + assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd"); + assert_eq!(label, "foo"); + assert!(uri.amount.is_none()); + assert!(uri.message.is_none()); + assert_eq!(uri.to_string(), "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo"); + } + + #[test] + fn rfc3986_non_empty_fragment_not_defined_in_bip21() { + let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo#&message=not%20part%20of%20a%20message"; + let uri = input.parse::>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap(); + let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap(); + assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd"); + assert_eq!(label, "foo"); + assert!(uri.amount.is_none()); + assert!(uri.message.is_none()); + assert_eq!(uri.to_string(), "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=foo"); + } } diff --git a/src/ser.rs b/src/ser.rs index 678b669..a77ca6a 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -59,9 +59,9 @@ impl<'a, W: fmt::Write> fmt::Write for EqSignChecker<'a, W> { /// > otherparam = qchar *qchar [ "=" *qchar ] /// > ``` /// ... -/// > Here, "qchar" corresponds to valid characters of an RFC 3986 URI > query -/// component, excluding the "=" and "&" characters, which this BIP > takes as -/// separators. +/// > Here, "qchar" corresponds to valid characters of an RFC 3986 URI query +/// > component, excluding the "=" and "&" characters, which this BIP takes as +/// > separators. /// /// [RFC 3986 Appendix A](https://www.rfc-editor.org/rfc/rfc3986#appendix-A): /// From 59f464cddca590944da8fb05d22f9c3be841894c Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 2 Dec 2024 13:30:00 -0500 Subject: [PATCH 06/12] Replace is_some_and with map_or for MSRV is_some_and is stable only in Rust 1.70.0 and greater. map_or(false, |s| s.eq_ignore_ascii_case(SCHEME)): - This method is used to handle the Option returned by get(). - If get() returns None (i.e., the string is too short), map_or() returns false. - If get() returns Some(s), it applies the closure |s| s.eq_ignore_ascii_case(SCHEME). - This closure checks if the substring s is equal to SCHEME, ignoring ASCII case differences. - If they are equal, it returns true; otherwise, it returns false. --- src/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index a5942fd..df5945a 100644 --- a/src/de.rs +++ b/src/de.rs @@ -25,7 +25,7 @@ impl<'a, T: DeserializeParams<'a>> Uri<'a, bitcoin::address::NetworkUnchecked, T return Err(Error::Uri(UriError(UriErrorInner::TooShort))); } - if !string.get(..SCHEME.len()).is_some_and(|s| s.eq_ignore_ascii_case(SCHEME)) { + if !string.get(..SCHEME.len()).map_or(false, |s| s.eq_ignore_ascii_case(SCHEME)) { return Err(Error::Uri(UriError(UriErrorInner::InvalidScheme))); } From 71a8ae2d2d3609c4adb1fc9e00b44e5098ccfa50 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 2 Dec 2024 10:53:30 -0500 Subject: [PATCH 07/12] `cargo clippy -all-targets --all-features --fix` --- src/de.rs | 6 +++--- src/lib.rs | 12 ++++++------ src/ser.rs | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/de.rs b/src/de.rs index df5945a..f70c810 100644 --- a/src/de.rs +++ b/src/de.rs @@ -93,7 +93,7 @@ impl<'a, T: DeserializeParams<'a>> Uri<'a, bitcoin::address::NetworkUnchecked, T } } -impl<'a, NetVal: NetworkValidation, T> Uri<'a, NetVal, T> { +impl Uri<'_, NetVal, T> { /// Makes the lifetime `'static` by converting all fields to owned. /// /// Note that this does **not** affect `extras`! @@ -293,7 +293,7 @@ impl std::error::Error for UriError { } /// **Warning**: this implementation may needlessly allocate, consider using `TryFrom<&str>` instead. -impl<'a, T: for<'de> DeserializeParams<'de>> core::str::FromStr for Uri<'a, bitcoin::address::NetworkUnchecked, T> { +impl DeserializeParams<'de>> core::str::FromStr for Uri<'_, bitcoin::address::NetworkUnchecked, T> { type Err = Error; fn from_str(s: &str) -> Result { @@ -310,7 +310,7 @@ impl<'a, T: DeserializeParams<'a>> TryFrom<&'a str> for Uri<'a, bitcoin::address } /// **Warning**: this implementation may needlessly allocate, consider using `TryFrom<&str>` instead. -impl<'a, T: for<'de> DeserializeParams<'de>> TryFrom for Uri<'a, bitcoin::address::NetworkUnchecked, T> { +impl DeserializeParams<'de>> TryFrom for Uri<'_, bitcoin::address::NetworkUnchecked, T> { type Error = Error; fn try_from(s: String) -> Result { diff --git a/src/lib.rs b/src/lib.rs index b60eba5..4faa0a0 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,7 +102,7 @@ where pub extras: Extras, } -impl<'a, NetVal: NetworkValidation, T: Default> Uri<'a, NetVal, T> { +impl Uri<'_, NetVal, T> { /// Creates an URI with defaults. /// /// This sets all fields except `address` to default values. @@ -118,7 +118,7 @@ impl<'a, NetVal: NetworkValidation, T: Default> Uri<'a, NetVal, T> { } } -impl<'a, NetVal: NetworkValidation, T> Uri<'a, NetVal, T> { +impl Uri<'_, NetVal, T> { /// Creates an URI with defaults. /// /// This sets all fields except `address` and `extras` to default values. @@ -194,7 +194,7 @@ impl<'a> From<&'a str> for Param<'a> { } /// Cheap conversion -impl<'a> From for Param<'a> { +impl From for Param<'_> { fn from(value: String) -> Self { Param(ParamInner::UnencodedString(Cow::Owned(value))) } @@ -212,7 +212,7 @@ impl<'a> From<&'a [u8]> for Param<'a> { /// Cheap conversion #[cfg(feature = "non-compliant-bytes")] #[cfg_attr(docsrs, doc(cfg(feature = "non-compliant-bytes")))] -impl<'a> From> for Param<'a> { +impl From> for Param<'_> { fn from(value: Vec) -> Self { Param(ParamInner::UnencodedBytes(Cow::Owned(value))) } @@ -315,7 +315,7 @@ impl DeserializationError for NoExtras { type Error = core::convert::Infallible; } -impl<'de> DeserializationState<'de> for EmptyState { +impl DeserializationState<'_> for EmptyState { type Value = NoExtras; fn is_param_known(&self, _key: &str) -> bool { @@ -331,7 +331,7 @@ impl<'de> DeserializationState<'de> for EmptyState { } } -impl<'a> SerializeParams for &'a NoExtras { +impl SerializeParams for &NoExtras { type Key = core::convert::Infallible; type Value = core::convert::Infallible; type Iterator = core::iter::Empty<(Self::Key, Self::Value)>; diff --git a/src/ser.rs b/src/ser.rs index a77ca6a..4a11e78 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -30,7 +30,7 @@ pub trait SerializeParams { /// Checks if the display implementation outputs `=` character. struct EqSignChecker<'a, W: fmt::Write>(W, &'a dyn fmt::Display); -impl<'a, W: fmt::Write> fmt::Write for EqSignChecker<'a, W> { +impl fmt::Write for EqSignChecker<'_, W> { fn write_str(&mut self, s: &str) -> fmt::Result { if s.contains('=') { panic!("key '{}' contains equal sign", self.1); @@ -128,7 +128,7 @@ impl fmt::Display for DisplayEncoder { /// This is private because people should generally only display values as decoded struct DisplayParam<'a>(&'a Param<'a>); -impl<'a> fmt::Display for DisplayParam<'a> { +impl fmt::Display for DisplayParam<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &(self.0).0 { // TODO: improve percent_encoding_rfc_3986 so that allocation can be avoided @@ -175,7 +175,7 @@ fn maybe_display_param(writer: &mut impl fmt::Write, key: impl fmt::Display, val /// Formats QR-code-optimized URI if alternate form (`{:#}`) is used. #[rustfmt::skip] -impl<'a, T> fmt::Display for Uri<'a, bitcoin::address::NetworkChecked, T> where for<'b> &'b T: SerializeParams { +impl fmt::Display for Uri<'_, bitcoin::address::NetworkChecked, T> where for<'b> &'b T: SerializeParams { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if f.alternate() { write!(f, "bitcoin:{:#}", self.address)?; From a827cb34e32cb12b311096bea2310fa57217ded2 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 2 Dec 2024 11:14:07 -0500 Subject: [PATCH 08/12] Move `'b` lifetime to `'a` since `'a` was elided --- src/ser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ser.rs b/src/ser.rs index 4a11e78..9451097 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -175,7 +175,7 @@ fn maybe_display_param(writer: &mut impl fmt::Write, key: impl fmt::Display, val /// Formats QR-code-optimized URI if alternate form (`{:#}`) is used. #[rustfmt::skip] -impl fmt::Display for Uri<'_, bitcoin::address::NetworkChecked, T> where for<'b> &'b T: SerializeParams { +impl fmt::Display for Uri<'_, bitcoin::address::NetworkChecked, T> where for<'a> &'a T: SerializeParams { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if f.alternate() { write!(f, "bitcoin:{:#}", self.address)?; From 9242659c35d9e3aea127394868069a2652cb7135 Mon Sep 17 00:00:00 2001 From: DanGould Date: Thu, 1 Aug 2024 17:36:50 -0400 Subject: [PATCH 09/12] Use rust MSRV 1.63.0 --- .github/workflows/rust.yml | 8 ++++++-- README.md | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4893ea4..4283aa1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -37,10 +37,10 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - rust: [stable, 1.56.1] + rust: [stable, 1.63.0] exclude: - os: macOS-latest - rust: 1.56.1 + rust: 1.63.0 runs-on: ${{ matrix.os }} steps: @@ -50,5 +50,9 @@ jobs: rust-version: ${{ matrix.rust }} - name: Checkout uses: actions/checkout@v2 + - name: Fixes for MSRV + if: matrix.rust == '1.63.0' + run: | + cargo update - name: Test run: cargo test diff --git a/README.md b/README.md index 631dbdf..65bbca4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The crate is `no_std` but does require `alloc`. ## MSRV -1.56.1 +1.63.0 ## License From 63062b1a0ed278ffea2cbf4f7de14b0e2c5deffe Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 2 Aug 2024 12:49:07 -0400 Subject: [PATCH 10/12] Act on: pull-request --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4283aa1..6420200 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,6 +1,6 @@ name: build -on: push +on: pull_request jobs: codestyle: From 8cc5ff77a15785fb9ead5445cb0a4e4c7cfedb5a Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 2 Dec 2024 13:07:08 -0500 Subject: [PATCH 11/12] Silence unused UriErerorInner dead code The error is only passed up via `source` when `std::error::Error` is available. When the feature is off, it's dead code. --- src/de.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/de.rs b/src/de.rs index f70c810..c5f3c48 100644 --- a/src/de.rs +++ b/src/de.rs @@ -234,6 +234,7 @@ impl std::error::Error for Error< pub struct UriError(UriErrorInner); #[derive(Debug, Clone)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] enum UriErrorInner { TooShort, InvalidScheme, From 2675825fbb81e4bed7e485bcf7356b4de1647a94 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 2 Dec 2024 13:13:51 -0500 Subject: [PATCH 12/12] Allow dead code for ParamBytes inner The code isn't actually dead, and is used in the bytes() function, but the compiler's dead code analysis doesn't fully track usage through feature flags and doesn't count if the pattern isn't considered a read of the field. --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4faa0a0..25af98c 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,6 +284,7 @@ enum ParamInner<'a> { /// The lifetime of this may be shorter than that of [`Param<'a>`]. #[cfg(feature = "non-compliant-bytes")] #[cfg_attr(docsrs, doc(cfg(feature = "non-compliant-bytes")))] +#[cfg_attr(feature = "non-compliant-bytes", allow(dead_code))] pub struct ParamBytes<'a>(ParamIterInner<'a, core::iter::Cloned>>); /// Iterator over decoded bytes inside paramter. @@ -291,6 +292,7 @@ pub struct ParamBytes<'a>(ParamIterInner<'a, core::iter::Cloned`]. #[cfg(feature = "non-compliant-bytes")] #[cfg_attr(docsrs, doc(cfg(feature = "non-compliant-bytes")))] +#[cfg_attr(feature = "non-compliant-bytes", allow(dead_code))] pub struct ParamBytesOwned<'a>(ParamIterInner<'a, Either>, alloc::vec::IntoIter>>); #[cfg(feature = "non-compliant-bytes")]