Skip to content
This repository was archived by the owner on Dec 17, 2025. It is now read-only.

Commit 24b2e7e

Browse files
committed
simplify interfaces
1 parent ab8422b commit 24b2e7e

File tree

8 files changed

+119
-144
lines changed

8 files changed

+119
-144
lines changed

README.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,30 @@ Inspired by [shiguredo/base32_clockwork][1] and [this gist][2].
1717
gleam add gbase32_clockwork
1818
```
1919
```gleam
20-
import gbase32_clockwork
21-
import gbase32_clockwork/options.{Lowercase}
20+
import gbase32_clockwork/gbase32
21+
import gleam/result
22+
import gleam/string
23+
import gleeunit/should
2224
2325
pub fn main() {
24-
let codebook = gbase32_clockwork.new([])
26+
// create a reusable encoder
27+
let encode = gbase32.new_encoder()
28+
29+
// by default, lowercase is emitted
30+
encode("foobar")
31+
|> should.equal(Ok("csqpyrk1e8"))
2532
26-
codebook.encode("foobar")
33+
// to emit as uppercase, simply uppercase the output string
34+
encode("foobar")
35+
|> result.map(string.uppercase)
2736
|> should.equal(Ok("CSQPYRK1E8"))
2837
29-
// to emit as lowercase
30-
let codebook_lc = gbase32_clockwork.new([Lowercase])
31-
32-
codebook.encode("foobar")
33-
|> should.equal(Ok("csqpyrk1e8"))
38+
// create a reusable encoder
39+
let decode = gbase32.new_encoder()
40+
41+
// a decoder will decode both upper and lowercase
42+
decode("CSQPYRG")
43+
|> should.equal(Ok("foob"))
3444
}
3545
```
3646

gleam.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = "gbase32_clockwork"
2-
version = "1.0.0"
2+
version = "2.0.0"
33
gleam = ">= 0.32.0"
44

55
description = "base32 clockwork in gleam"

manifest.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# You typically do not need to edit this file
33

44
packages = [
5-
{ name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" },
6-
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
5+
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
6+
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
77
]
88

99
[requirements]

src/gbase32_clockwork.gleam

Lines changed: 0 additions & 70 deletions
This file was deleted.

src/gbase32_clockwork/codebook.gleam

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,60 @@
1-
import gbase32_clockwork/codebook.{type DecodeBook, type EncodeBook}
21
import gleam/bit_array
32
import gleam/bytes_builder.{type BytesBuilder}
4-
import gleam/dict
3+
import gleam/dict.{type Dict}
54
import gleam/int.{bitwise_shift_left}
65
import gleam/list
76
import gleam/result
87
import gleam/string
98

9+
const clockwork_codebook = "0123456789abcdefghjkmnpqrstvwxyz"
10+
11+
type EncodeBook =
12+
Dict(Int, String)
13+
14+
type DecodeBook =
15+
Dict(String, Int)
16+
17+
pub type EncodeFn =
18+
fn(String) -> Result(String, String)
19+
20+
pub type DecodeFn =
21+
fn(String) -> Result(String, String)
22+
23+
/// Create a new encoder that can be be reused to encode strings in base32 clockwork
24+
///
25+
pub fn new_encoder() -> EncodeFn {
26+
let encodebook: EncodeBook =
27+
clockwork_codebook
28+
|> string.to_graphemes()
29+
|> list.index_map(fn(x, i) { #(i, x) })
30+
|> dict.from_list()
31+
32+
encode(encodebook, _)
33+
}
34+
35+
/// Create a new decoder that can be be reused to decode strings from base32 clockwork
36+
///
37+
pub fn new_decoder() -> DecodeFn {
38+
let decodebook: DecodeBook =
39+
clockwork_codebook
40+
|> string.to_graphemes()
41+
|> list.index_map(fn(x, i) { #(x, i) })
42+
|> dict.from_list()
43+
|> dict.merge(
44+
// add some aliases to help decode user entry errors
45+
dict.from_list([
46+
#("O", 0),
47+
#("o", 0),
48+
#("I", 1),
49+
#("i", 1),
50+
#("L", 1),
51+
#("l", 1),
52+
]),
53+
)
54+
55+
decode(decodebook, _)
56+
}
57+
1058
fn to_symbol(c: Int, codebook: EncodeBook) -> Result(String, String) {
1159
dict.get(codebook, c)
1260
|> result.map_error(fn(_) { "Encoding outside range" })
@@ -105,7 +153,7 @@ fn decode_rec(
105153
}
106154
}
107155

108-
pub fn encode(codebook: EncodeBook, input: String) -> Result(String, String) {
156+
fn encode(codebook: EncodeBook, input: String) -> Result(String, String) {
109157
let encoded =
110158
input
111159
|> bit_array.from_string()
@@ -122,10 +170,11 @@ pub fn encode(codebook: EncodeBook, input: String) -> Result(String, String) {
122170
}
123171
}
124172

125-
pub fn decode(codebook: DecodeBook, input: String) -> Result(String, String) {
173+
fn decode(codebook: DecodeBook, input: String) -> Result(String, String) {
126174
let decoded =
127175
input
128176
|> string.trim()
177+
|> string.lowercase
129178
|> string.to_graphemes()
130179
|> list.filter(fn(x) { x != "=" })
131180
|> decode_rec(codebook, _, 0, Ok(bytes_builder.new()))

src/gbase32_clockwork/options.gleam

Lines changed: 0 additions & 4 deletions
This file was deleted.

test/gbase32_clockwork_test.gleam

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import gbase32_clockwork
2-
import gbase32_clockwork/options.{Lowercase}
1+
import gbase32_clockwork/gbase32
2+
import gleam/result
3+
import gleam/string
34
import gleeunit
45
import gleeunit/should
56

@@ -8,106 +9,113 @@ pub fn main() {
89
}
910

1011
pub fn encode_uppercase_test() {
11-
let codebook = gbase32_clockwork.new([])
12+
let encode = gbase32.new_encoder()
1213

13-
codebook.encode("")
14+
encode("")
1415
|> should.equal(Ok(""))
1516

16-
codebook.encode("f")
17+
encode("f")
18+
|> result.map(string.uppercase)
1719
|> should.equal(Ok("CR"))
1820

19-
codebook.encode("fo")
21+
encode("fo")
22+
|> result.map(string.uppercase)
2023
|> should.equal(Ok("CSQG"))
2124

22-
codebook.encode("foo")
25+
encode("foo")
26+
|> result.map(string.uppercase)
2327
|> should.equal(Ok("CSQPY"))
2428

25-
codebook.encode("foob")
29+
encode("foob")
30+
|> result.map(string.uppercase)
2631
|> should.equal(Ok("CSQPYRG"))
2732

28-
codebook.encode("fooba")
33+
encode("fooba")
34+
|> result.map(string.uppercase)
2935
|> should.equal(Ok("CSQPYRK1"))
3036

31-
codebook.encode("foobar")
37+
encode("foobar")
38+
|> result.map(string.uppercase)
3239
|> should.equal(Ok("CSQPYRK1E8"))
3340

34-
codebook.encode("Wow, it works!")
41+
encode("Wow, it works!")
42+
|> result.map(string.uppercase)
3543
|> should.equal(Ok("AXQQEB10D5T20XVFE9NQ688"))
3644
}
3745

3846
pub fn encode_lowercase_test() {
39-
let codebook = gbase32_clockwork.new([Lowercase])
47+
let encode = gbase32.new_encoder()
4048

41-
codebook.encode("")
49+
encode("")
4250
|> should.equal(Ok(""))
4351

44-
codebook.encode("f")
52+
encode("f")
4553
|> should.equal(Ok("cr"))
4654

47-
codebook.encode("fo")
55+
encode("fo")
4856
|> should.equal(Ok("csqg"))
4957

50-
codebook.encode("foo")
58+
encode("foo")
5159
|> should.equal(Ok("csqpy"))
5260

53-
codebook.encode("foob")
61+
encode("foob")
5462
|> should.equal(Ok("csqpyrg"))
5563

56-
codebook.encode("fooba")
64+
encode("fooba")
5765
|> should.equal(Ok("csqpyrk1"))
5866

59-
codebook.encode("foobar")
67+
encode("foobar")
6068
|> should.equal(Ok("csqpyrk1e8"))
6169

62-
codebook.encode("Wow, it works!")
70+
encode("Wow, it works!")
6371
|> should.equal(Ok("axqqeb10d5t20xvfe9nq688"))
6472
}
6573

6674
pub fn decode_test() {
67-
let codebook = gbase32_clockwork.new([])
75+
let decode = gbase32.new_decoder()
6876

69-
codebook.decode("")
77+
decode("")
7078
|> should.equal(Ok(""))
7179

72-
codebook.decode("CR")
80+
decode("CR")
7381
|> should.equal(Ok("f"))
7482

75-
codebook.decode("CSQG")
83+
decode("CSQG")
7684
|> should.equal(Ok("fo"))
7785

78-
codebook.decode("CSQPY")
86+
decode("CSQPY")
7987
|> should.equal(Ok("foo"))
8088

81-
codebook.decode("CSQPYRG")
89+
decode("CSQPYRG")
8290
|> should.equal(Ok("foob"))
8391

84-
codebook.decode("CSQPYRK1")
92+
decode("CSQPYRK1")
8593
|> should.equal(Ok("fooba"))
8694

87-
codebook.decode("CSQPYRK1E8")
95+
decode("CSQPYRK1E8")
8896
|> should.equal(Ok("foobar"))
8997

90-
codebook.decode(" CSQPYRK1E8 ")
98+
decode(" CSQPYRK1E8 ")
9199
|> should.equal(Ok("foobar"))
92100

93-
codebook.decode("CSQPYRK1E8====")
101+
decode("CSQPYRK1E8====")
94102
|> should.equal(Ok("foobar"))
95103

96-
codebook.decode(" CSQPYRK1E8==== ")
104+
decode(" CSQPYRK1E8==== ")
97105
|> should.equal(Ok("foobar"))
98106

99-
codebook.decode("AXQQEB10D5T20XVFE9NQ688")
107+
decode("AXQQEB10D5T20XVFE9NQ688")
100108
|> should.equal(Ok("Wow, it works!"))
101109

102-
codebook.decode("cSqPy")
110+
decode("cSqPy")
103111
|> should.equal(Ok("foo"))
104112

105-
codebook.decode("CSQPYRKi")
113+
decode("CSQPYRKi")
106114
|> should.equal(Ok("fooba"))
107115

108-
codebook.decode("csqpyrk1e8")
116+
decode("csqpyrk1e8")
109117
|> should.equal(Ok("foobar"))
110118

111-
codebook.decode("C-SQPY")
119+
decode("C-SQPY")
112120
|> should.equal(Error("Encoding outside range"))
113121
}

0 commit comments

Comments
 (0)