Skip to content

Commit

Permalink
Migrate to gleam/dynamic/decode
Browse files Browse the repository at this point in the history
  • Loading branch information
silby committed Dec 28, 2024
1 parent 3457d47 commit ee38e03
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 38 deletions.
2 changes: 1 addition & 1 deletion gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ allow_write = [
]

[dependencies]
gleam_stdlib = "~> 0.25"
gleam_stdlib = "~> 0.51"
esqlite = "~> 0.8"

[dev-dependencies]
Expand Down
6 changes: 3 additions & 3 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
# You typically do not need to edit this file

packages = [
{ name = "esqlite", version = "0.8.8", build_tools = ["rebar3"], requirements = [], otp_app = "esqlite", source = "hex", outer_checksum = "374902457C7D94DC9409C98D3BDD1CA0D50A60DC9F3BDF1FD8EB74C0DCDF02D6" },
{ name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" },
{ name = "esqlite", version = "0.8.9", build_tools = ["rebar3"], requirements = [], otp_app = "esqlite", source = "hex", outer_checksum = "465AE9AE28AE4192EA54C829FDC90C320447D439A9B2E10946621672FC6A6F8C" },
{ name = "gleam_stdlib", version = "0.51.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "14AFA8D3DDD7045203D422715DBB822D1725992A31DF35A08D97389014B74B68" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
]

[requirements]
esqlite = { version = "~> 0.8" }
gleam_stdlib = { version = "~> 0.25" }
gleam_stdlib = { version = "~> 0.51" }
gleeunit = { version = "~> 1.0" }
19 changes: 10 additions & 9 deletions src/sqlight.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gleam/dynamic.{type Decoder, type Dynamic}
import gleam/dynamic/decode.{type Decoder, type Dynamic}
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/result
Expand Down Expand Up @@ -383,7 +383,7 @@ pub fn query(
) -> Result(List(t), Error) {
use rows <- result.then(run_query(sql, connection, arguments))
use rows <- result.then(
list.try_map(over: rows, with: decoder)
list.try_map(over: rows, with: fn(row) { decode.run(row, decoder) })
|> result.map_error(decode_error),
)
Ok(rows)
Expand Down Expand Up @@ -468,16 +468,17 @@ pub fn null() -> Value
///
/// Decodes 0 as `False` and any other integer as `True`.
///
pub fn decode_bool(value: Dynamic) -> Result(Bool, List(dynamic.DecodeError)) {
case dynamic.int(value) {
Ok(0) -> Ok(False)
Ok(_) -> Ok(True)
Error(e) -> Error(e)
pub fn decode_bool() -> Decoder(Bool) {
use b <- decode.then(decode.int)

case b {
0 -> decode.success(False)
_ -> decode.success(True)
}
}

fn decode_error(errors: List(dynamic.DecodeError)) -> Error {
let assert [dynamic.DecodeError(expected, actual, path), ..] = errors
fn decode_error(errors: List(decode.DecodeError)) -> Error {
let assert [decode.DecodeError(expected, actual, path), ..] = errors
let path = string.join(path, ".")
let message =
"Decoder failed, expected "
Expand Down
64 changes: 39 additions & 25 deletions test/sqlight_test.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gleam/dynamic
import gleam/dynamic/decode
import gleam/list
import gleam/option
import gleeunit
Expand Down Expand Up @@ -54,18 +54,23 @@ pub fn with_connection_test() {
pub fn query_1_test() {
use conn <- connect()
let assert Ok([#(1, 2, 3), #(4, 5, 6)]) =
sqlight.query(
"select 1, 2, 3 union all select 4, 5, 6",
conn,
[],
dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int),
)
sqlight.query("select 1, 2, 3 union all select 4, 5, 6", conn, [], {
use one <- decode.field(0, decode.int)
use two <- decode.field(1, decode.int)
use three <- decode.field(2, decode.int)
decode.success(#(one, two, three))
})
}

pub fn query_2_test() {
use conn <- connect()
let assert Ok([1337]) =
sqlight.query("select 1337", conn, [], dynamic.element(0, dynamic.int))
sqlight.query(
"select 1337",
conn,
[],
decode.field(0, decode.int, decode.success),
)
}

pub fn bind_int_test() {
Expand All @@ -75,7 +80,7 @@ pub fn bind_int_test() {
"select ?",
conn,
[sqlight.int(12_345)],
dynamic.element(0, dynamic.int),
decode.field(0, decode.int, decode.success),
)
}

Expand All @@ -86,7 +91,7 @@ pub fn bind_float_test() {
"select ?",
conn,
[sqlight.float(12_345.6789)],
dynamic.element(0, dynamic.float),
decode.field(0, decode.float, decode.success),
)
}

Expand All @@ -97,19 +102,18 @@ pub fn bind_text_test() {
"select ?",
conn,
[sqlight.text("hello")],
dynamic.element(0, dynamic.string),
decode.field(0, decode.string, decode.success),
)
}

pub fn bind_blob_test() {
use conn <- connect()
let assert Ok([#(<<123, 0>>, "blob")]) =
sqlight.query(
"select ?1, typeof(?1)",
conn,
[sqlight.blob(<<123, 0>>)],
dynamic.tuple2(dynamic.bit_array, dynamic.string),
)
sqlight.query("select ?1, typeof(?1)", conn, [sqlight.blob(<<123, 0>>)], {
use ary <- decode.field(0, decode.bit_array)
use str <- decode.field(1, decode.string)
decode.success(#(ary, str))
})
}

pub fn bind_null_test() {
Expand All @@ -119,7 +123,7 @@ pub fn bind_null_test() {
"select ?",
conn,
[sqlight.null()],
dynamic.element(0, dynamic.optional(dynamic.int)),
decode.field(0, decode.optional(decode.int), decode.success),
)
}

Expand All @@ -130,7 +134,7 @@ pub fn bind_bool_test() {
"select ?",
conn,
[sqlight.bool(True)],
dynamic.element(0, sqlight.decode_bool),
decode.field(0, sqlight.decode_bool(), decode.success),
)
}

Expand All @@ -144,7 +148,7 @@ pub fn exec_test() {
"select name from cats",
conn,
[],
dynamic.element(0, dynamic.string),
decode.field(0, decode.string, decode.success),
)
}

Expand All @@ -157,7 +161,11 @@ pub fn exec_fail_test() {

pub fn readme_example_test() {
use conn <- sqlight.with_connection(":memory:")
let cat_decoder = dynamic.tuple2(dynamic.string, dynamic.int)
let cat_decoder = {
use name <- decode.field(0, decode.string)
use age <- decode.field(1, decode.int)
decode.success(#(name, age))
}

let sql =
"
Expand Down Expand Up @@ -242,7 +250,13 @@ pub fn decode_error_test() {
sqlight.GenericError,
"Decoder failed, expected String, got Int in 0",
-1,
)) = sqlight.query("select 1", conn, [], dynamic.element(0, dynamic.string))
)) =
sqlight.query(
"select 1",
conn,
[],
decode.field(0, decode.string, decode.success),
)
}

pub fn query_error_test() {
Expand All @@ -253,7 +267,7 @@ pub fn query_error_test() {
"this isn't a valid query",
conn,
[],
dynamic.element(0, dynamic.int),
decode.field(0, decode.int, decode.success),
)
}

Expand All @@ -265,14 +279,14 @@ pub fn bind_nullable_test() {
"select ?",
conn,
[sqlight.nullable(sqlight.int, option.Some(12_345))],
dynamic.element(0, dynamic.optional(dynamic.int)),
decode.field(0, decode.optional(decode.int), decode.success),
)

let assert Ok([option.None]) =
sqlight.query(
"select ?",
conn,
[sqlight.nullable(sqlight.int, option.None)],
dynamic.element(0, dynamic.optional(dynamic.int)),
decode.field(0, decode.optional(decode.int), decode.success),
)
}

0 comments on commit ee38e03

Please sign in to comment.