From 26d7d90e2bded8ae2481e5981f1ceff14ff68e5d Mon Sep 17 00:00:00 2001 From: Richard Viney Date: Thu, 6 Feb 2025 13:15:57 +1300 Subject: [PATCH 1/3] Add bit_array.to_string_lossy --- CHANGELOG.md | 4 ++++ src/gleam/bit_array.gleam | 40 +++++++++++++++++++++++++++++++++ test/gleam/bit_array_test.gleam | 13 +++++++++++ 3 files changed, 57 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c42f729d..7bfe1bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- The `bit_array` module gains the `to_string_lossy` function. + ## v0.66.0 - 2025-10-21 - The `tap` function from the `function` module has been deprecated. diff --git a/src/gleam/bit_array.gleam b/src/gleam/bit_array.gleam index 544b74af..65eb2e44 100644 --- a/src/gleam/bit_array.gleam +++ b/src/gleam/bit_array.gleam @@ -95,6 +95,46 @@ pub fn to_string(bits: BitArray) -> Result(String, Nil) { @external(erlang, "gleam_stdlib", "identity") fn unsafe_to_string(a: BitArray) -> String +/// Converts a bit array to a string. Invalid bits are passed to the provided +/// callback and its result is included in the final string in place of the +/// invalid data. +/// +/// ## Examples +/// +/// ```gleam +/// to_string_lossy(<<"A":utf8, 0x80, "1":utf8, 0:size(5)>>, fn(_) { "�" }) +/// // -> "A�1�" +/// ``` +/// +pub fn to_string_lossy( + bits: BitArray, + map_invalid_bits: fn(BitArray) -> String, +) -> String { + to_string_lossy_impl(bits, map_invalid_bits, "") +} + +fn to_string_lossy_impl( + bits: BitArray, + map_invalid_bits: fn(BitArray) -> String, + acc: String, +) -> String { + case bits { + <<>> -> acc + + <> -> + to_string_lossy_impl( + rest, + map_invalid_bits, + acc <> string.from_utf_codepoints([x]), + ) + + <> -> + to_string_lossy_impl(rest, map_invalid_bits, acc <> map_invalid_bits(x)) + + _ -> acc <> map_invalid_bits(bits) + } +} + /// Creates a new bit array by joining multiple binaries. /// /// ## Examples diff --git a/test/gleam/bit_array_test.gleam b/test/gleam/bit_array_test.gleam index 3dd43e98..05c04b13 100644 --- a/test/gleam/bit_array_test.gleam +++ b/test/gleam/bit_array_test.gleam @@ -135,6 +135,19 @@ pub fn to_string_test() { assert bit_array.to_string(x) == Ok("ø") } +pub fn to_string_lossy_test() { + assert bit_array.to_string_lossy(<<>>, fn(_) { "�" }) == "" + + assert bit_array.to_string_lossy(<<0x80, "A":utf8, 0x81>>, fn(_) { "�" }) + == "�A�" + + // Test some codepoints that require 2/3/4 bytes to be stored as UTF-8 + assert bit_array.to_string_lossy(<<"£И한𐍈":utf8>>, fn(_) { "�" }) == "£И한𐍈" + + // Test unaligned bit array + assert bit_array.to_string_lossy(<<"ø":utf8, 2:4>>, fn(_) { "�" }) == "ø�" +} + pub fn is_utf8_test() { assert bit_array.is_utf8(<<>>) From 19fb9cd9b6cef798cfb1dfa56a384c15f3f7cd59 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Fri, 24 Oct 2025 12:53:25 +0100 Subject: [PATCH 2/3] Update Gleam --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d18ec50..dcdc4113 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - uses: erlef/setup-beam@v1 with: otp-version: ${{ matrix.erlang_version }} - gleam-version: "1.11.0" + gleam-version: "1.13.0" - run: gleam test --target erlang - run: gleam format --check src test @@ -44,7 +44,7 @@ jobs: - uses: erlef/setup-beam@v1 with: otp-version: "28" - gleam-version: "1.11.0" + gleam-version: "1.13.0" - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node_version }} @@ -62,7 +62,7 @@ jobs: - uses: erlef/setup-beam@v1 with: otp-version: "28" - gleam-version: "1.11.0" + gleam-version: "1.13.0" - uses: oven-sh/setup-bun@v2 with: bun-version: ${{ matrix.bun_version }} @@ -80,7 +80,7 @@ jobs: - uses: erlef/setup-beam@v1 with: otp-version: "28" - gleam-version: "1.11.0" + gleam-version: "1.13.0" - uses: denoland/setup-deno@v1 with: deno-version: ${{ matrix.deno_version }} From 0401b988aa69d8ceab5cae1871ef28543681edab Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Fri, 24 Oct 2025 12:54:42 +0100 Subject: [PATCH 3/3] Format --- test/gleeunit/should.gleam | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/gleeunit/should.gleam b/test/gleeunit/should.gleam index 11cd82b7..99cd16c4 100644 --- a/test/gleeunit/should.gleam +++ b/test/gleeunit/should.gleam @@ -8,11 +8,11 @@ pub fn equal(a: t, b: t) -> Nil { True -> Nil _ -> panic as string.concat([ - "\n", - string.inspect(a), - "\nshould equal\n", - string.inspect(b), - ]) + "\n", + string.inspect(a), + "\nshould equal\n", + string.inspect(b), + ]) } } @@ -21,11 +21,11 @@ pub fn not_equal(a: t, b: t) -> Nil { True -> Nil _ -> panic as string.concat([ - "\n", - string.inspect(a), - "\nshould not equal\n", - string.inspect(b), - ]) + "\n", + string.inspect(a), + "\nshould not equal\n", + string.inspect(b), + ]) } }