From f6fbe2d39613d999a9651b019eeca0ebfa332797 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:11:03 +1000 Subject: [PATCH] Fix render of AssetCode12 to JSON when shorter than 5 chars --- src/curr/str.rs | 19 +++++++++++++++---- tests/str.rs | 28 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/curr/str.rs b/src/curr/str.rs index c00d65bb..b5641d6c 100644 --- a/src/curr/str.rs +++ b/src/curr/str.rs @@ -291,10 +291,21 @@ impl core::str::FromStr for AssetCode12 { impl core::fmt::Display for AssetCode12 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - if let Some(last_idx) = self.0.iter().rposition(|c| *c != 0) { - for b in escape_bytes::Escape::new(&self.0[..=last_idx]) { - write!(f, "{}", b as char)?; - } + // AssetCode12's are always rendered as at least 5 characters, because + // any asset code shorter than 5 characters is an AssetCode4. + // AssetCode12 contains a fixed length 12-byte array, and the constant + // and slices in this function never operate out-of-bounds because of + // that. + const MIN_LENGTH: usize = 5; + let len = MIN_LENGTH + + self + .0 + .iter() + .skip(MIN_LENGTH) + .rposition(|c| *c != 0) + .map_or(0, |last_idx| last_idx + 1); + for b in escape_bytes::Escape::new(&self.0[..len]) { + write!(f, "{}", b as char)?; } Ok(()) } diff --git a/tests/str.rs b/tests/str.rs index 1ffb2e99..7d8eb63d 100644 --- a/tests/str.rs +++ b/tests/str.rs @@ -450,11 +450,11 @@ fn asset_code_12_from_str() { #[test] #[rustfmt::skip] fn asset_code_12_to_string() { - assert_eq!(AssetCode12(*b"\0\0\0\0\0\0\0\0\0\0\0\0").to_string(), ""); - assert_eq!(AssetCode12(*b"a\0\0\0\0\0\0\0\0\0\0\0").to_string(), "a"); - assert_eq!(AssetCode12(*b"ab\0\0\0\0\0\0\0\0\0\0").to_string(), "ab"); - assert_eq!(AssetCode12(*b"abc\0\0\0\0\0\0\0\0\0").to_string(), "abc"); - assert_eq!(AssetCode12(*b"abcd\0\0\0\0\0\0\0\0").to_string(), "abcd"); + assert_eq!(AssetCode12(*b"\0\0\0\0\0\0\0\0\0\0\0\0").to_string(), r"\0\0\0\0\0"); + assert_eq!(AssetCode12(*b"a\0\0\0\0\0\0\0\0\0\0\0").to_string(), r"a\0\0\0\0"); + assert_eq!(AssetCode12(*b"ab\0\0\0\0\0\0\0\0\0\0").to_string(), r"ab\0\0\0"); + assert_eq!(AssetCode12(*b"abc\0\0\0\0\0\0\0\0\0").to_string(), r"abc\0\0"); + assert_eq!(AssetCode12(*b"abcd\0\0\0\0\0\0\0\0").to_string(), r"abcd\0"); assert_eq!(AssetCode12(*b"abcde\0\0\0\0\0\0\0").to_string(), "abcde"); assert_eq!(AssetCode12(*b"abcdef\0\0\0\0\0\0").to_string(), "abcdef"); assert_eq!(AssetCode12(*b"abcdefg\0\0\0\0\0").to_string(), "abcdefg"); @@ -465,10 +465,10 @@ fn asset_code_12_to_string() { assert_eq!(AssetCode12(*b"abcdefghijkl").to_string(), "abcdefghijkl"); // Preserve as much of the code as possible, even if it contains nul bytes. - assert_eq!(AssetCode12(*b"a\0cd\0\0\0\0\0\0\0\0").to_string(), r"a\0cd"); + assert_eq!(AssetCode12(*b"a\0cd\0\0\0\0\0\0\0\0").to_string(), r"a\0cd\0"); // Replace bytes that are not valid utf8 with the replacement character � and preserve length. - assert_eq!(AssetCode12(*b"a\xc3\x28d\0\0\0\0\0\0\0\0").to_string(), r"a\xc3(d"); + assert_eq!(AssetCode12(*b"a\xc3\x28d\0\0\0\0\0\0\0\0").to_string(), r"a\xc3(d\0"); assert_eq!(AssetCode12(*b"a\xc3\xc3\x28d\0\0\0\0\0\0\0").to_string(), r"a\xc3\xc3(d"); } @@ -502,11 +502,11 @@ fn asset_code_to_string() { assert_eq!(AssetCode::CreditAlphanum4(AssetCode4(*b"abc\0")).to_string(), "abc"); assert_eq!(AssetCode::CreditAlphanum4(AssetCode4(*b"abcd")).to_string(), "abcd"); - assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"\0\0\0\0\0\0\0\0\0\0\0\0")).to_string(), ""); - assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"a\0\0\0\0\0\0\0\0\0\0\0")).to_string(), "a"); - assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"ab\0\0\0\0\0\0\0\0\0\0")).to_string(), "ab"); - assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"abc\0\0\0\0\0\0\0\0\0")).to_string(), "abc"); - assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"abcd\0\0\0\0\0\0\0\0")).to_string(), "abcd"); + assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"\0\0\0\0\0\0\0\0\0\0\0\0")).to_string(), r"\0\0\0\0\0"); + assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"a\0\0\0\0\0\0\0\0\0\0\0")).to_string(), r"a\0\0\0\0"); + assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"ab\0\0\0\0\0\0\0\0\0\0")).to_string(), r"ab\0\0\0"); + assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"abc\0\0\0\0\0\0\0\0\0")).to_string(), r"abc\0\0"); + assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"abcd\0\0\0\0\0\0\0\0")).to_string(), r"abcd\0"); assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"abcde\0\0\0\0\0\0\0")).to_string(), "abcde"); assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"abcdef\0\0\0\0\0\0")).to_string(), "abcdef"); assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"abcdefg\0\0\0\0\0")).to_string(), "abcdefg"); @@ -518,11 +518,11 @@ fn asset_code_to_string() { // Preserve as much of the code as possible, even if it contains nul bytes. assert_eq!(AssetCode::CreditAlphanum4(AssetCode4(*b"a\0cd")).to_string(), r"a\0cd"); - assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"a\0cd\0\0\0\0\0\0\0\0")).to_string(), r"a\0cd"); + assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"a\0cd\0\0\0\0\0\0\0\0")).to_string(), r"a\0cd\0"); // Replace bytes that are not valid utf8 with the replacement character � and preserve length. assert_eq!(AssetCode::CreditAlphanum4(AssetCode4(*b"a\xc3\x28d")).to_string(), r"a\xc3(d"); - assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"a\xc3\x28d\0\0\0\0\0\0\0\0")).to_string(), r"a\xc3(d"); + assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"a\xc3\x28d\0\0\0\0\0\0\0\0")).to_string(), r"a\xc3(d\0"); assert_eq!(AssetCode::CreditAlphanum12(AssetCode12(*b"a\xc3\xc3\x28d\0\0\0\0\0\0\0")).to_string(), r"a\xc3\xc3(d"); }