Skip to content

Commit 198cb1f

Browse files
committed
Deno support
1 parent 98bfddf commit 198cb1f

9 files changed

+229
-146
lines changed

.vscode/settings.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"deno.enable": true,
3+
"deno.lint": false,
4+
}

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## v0.4.0 - 2023-01-08
4+
5+
-
6+
- The `status` function has been removed.
7+
38
## v0.4.0 - 2023-01-02
49

510
- The error type now contains more detail.

gleam.toml

+15-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,23 @@ links = [
99
{ title = "Sponsor", href = "https://github.com/sponsors/lpil" },
1010
]
1111

12+
[javascript]
13+
runtime = "deno"
14+
15+
[javascript.deno]
16+
allow_read = [
17+
"gleam.toml",
18+
"test",
19+
"build",
20+
"tmp",
21+
]
22+
allow_write = [
23+
"tmp",
24+
]
25+
1226
[dependencies]
1327
gleam_stdlib = "~> 0.25"
1428
esqlite = "~> 0.8"
1529

1630
[dev-dependencies]
17-
gleeunit = "~> 0.7"
31+
gleeunit = "~> 0.8"

manifest.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
packages = [
55
{ name = "esqlite", version = "0.8.5", build_tools = ["rebar3"], requirements = [], otp_app = "esqlite", source = "hex", outer_checksum = "75F0DCAB190A09842B1D9305765C026D8AA2ED7C98EFF272DFB529CAC99B0742" },
66
{ name = "gleam_stdlib", version = "0.25.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "AD0F89928E0B919C8F8EDF640484633B28DBF88630A9E6AE504617A3E3E5B9A2" },
7-
{ name = "gleeunit", version = "0.8.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "A1170754BF54F5DD6E9EF392FB1DC612528B007CCBE41B52F0C5453254708490" },
7+
{ name = "gleeunit", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "37F8904B6844A99A2C041E56B2E08D464AC2529E68EB0A3FA4AD93745E0A3A59" },
88
]
99

1010
[requirements]
1111
esqlite = "~> 0.8"
1212
gleam_stdlib = "~> 0.25"
13-
gleeunit = "~> 0.7"
13+
gleeunit = "~> 0.8"

src/sqlight.gleam

+87-80
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,6 @@ pub type Stats {
1313
Stats(used: Int, highwater: Int)
1414
}
1515

16-
pub type StatusInfo {
17-
StatusInfo(
18-
memory_used: Stats,
19-
pagecache_used: Stats,
20-
pagecache_overflow: Stats,
21-
malloc_size: Stats,
22-
parser_stack: Stats,
23-
pagecache_size: Stats,
24-
malloc_count: Stats,
25-
)
26-
}
27-
2816
pub type Error {
2917
SqlightError(
3018
code: ErrorCode,
@@ -125,8 +113,6 @@ pub type ErrorCode {
125113
IoerrRdlock
126114
}
127115

128-
external type Charlist
129-
130116
/// Convert an `Error` to an error code int.
131117
///
132118
/// See the SQLite documentation for the full list of error codes.
@@ -309,19 +295,21 @@ pub fn error_code_from_int(code: Int) -> ErrorCode {
309295
}
310296
}
311297

312-
external type NilResult
313-
314-
external fn open_(Charlist) -> Result(Connection, Int) =
315-
"esqlite3" "open"
298+
if erlang {
299+
external fn open_(String) -> Result(Connection, Error) =
300+
"sqlight_ffi" "open"
316301

317-
external fn normalise_result(NilResult) -> Result(Nil, Int) =
318-
"sqlight_ffi" "normalise_result"
302+
external fn close_(Connection) -> Result(Nil, Error) =
303+
"sqlight_ffi" "close"
304+
}
319305

320-
external fn string_to_charlist(String) -> Charlist =
321-
"unicode" "characters_to_list"
306+
if javascript {
307+
external fn open_(String) -> Result(Connection, Error) =
308+
"./sqlight_ffi.js" "open"
322309

323-
external fn close_(Connection) -> NilResult =
324-
"esqlite3" "close"
310+
external fn close_(Connection) -> Result(Nil, Error) =
311+
"./sqlight_ffi.js" "close"
312+
}
325313

326314
/// Open a connection to a SQLite database.
327315
///
@@ -350,12 +338,7 @@ external fn close_(Connection) -> NilResult =
350338
/// ```
351339
///
352340
pub fn open(path: String) -> Result(Connection, Error) {
353-
path
354-
|> string_to_charlist
355-
|> open_
356-
|> result.map_error(fn(i) {
357-
SqlightError(code: error_code_from_int(i), message: "", offset: -1)
358-
})
341+
open_(path)
359342
}
360343

361344
/// Close a connection to a SQLite database.
@@ -365,10 +348,7 @@ pub fn open(path: String) -> Result(Connection, Error) {
365348
/// information: <https://www.sqlite.org/c3ref/close.html>.
366349
///
367350
pub fn close(connection: Connection) -> Result(Nil, Error) {
368-
connection
369-
|> close_
370-
|> normalise_result
371-
|> result.map_error(construct_error(connection, _))
351+
close_(connection)
372352
}
373353

374354
/// Open a connection to a SQLite database and execute a function with it, then
@@ -395,14 +375,8 @@ pub fn with_connection(path: String, f: fn(Connection) -> a) -> a {
395375
value
396376
}
397377

398-
/// Get all internal status information for SQLite.
399-
///
400-
pub external fn status() -> StatusInfo =
401-
"sqlight_ffi" "status"
402-
403378
pub fn exec(sql: String, on connection: Connection) -> Result(Nil, Error) {
404-
run_exec(connection, sql)
405-
|> result.map_error(construct_error(connection, _))
379+
exec_(sql, connection)
406380
}
407381

408382
pub fn query(
@@ -411,11 +385,7 @@ pub fn query(
411385
with arguments: List(Value),
412386
expecting decoder: Decoder(t),
413387
) -> Result(List(t), Error) {
414-
use rows <-
415-
result.then(
416-
run_query(connection, sql, arguments)
417-
|> result.map_error(construct_error(connection, _)),
418-
)
388+
use rows <- result.then(run_query(connection, sql, arguments))
419389
use rows <-
420390
result.then(
421391
list.try_map(over: rows, with: decoder)
@@ -424,39 +394,81 @@ pub fn query(
424394
Ok(rows)
425395
}
426396

427-
external fn run_query(
428-
Connection,
429-
String,
430-
List(Value),
431-
) -> Result(List(Dynamic), Int) =
432-
"sqlight_ffi" "query"
397+
if erlang {
398+
external fn run_query(
399+
Connection,
400+
String,
401+
List(Value),
402+
) -> Result(List(Dynamic), Error) =
403+
"sqlight_ffi" "query"
404+
}
405+
406+
if javascript {
407+
external fn run_query(
408+
Connection,
409+
String,
410+
List(Value),
411+
) -> Result(List(Dynamic), Error) =
412+
"./sqlight_ffi.js" "query"
413+
}
414+
415+
if erlang {
416+
external fn coerce_value(a) -> Value =
417+
"sqlight_ffi" "coerce_value"
418+
}
433419

434-
external fn run_exec(Connection, String) -> Result(Nil, Int) =
435-
"sqlight_ffi" "exec"
420+
if javascript {
421+
external fn coerce_value(a) -> Value =
422+
"./sqlight_ffi.js" "coerce_value"
423+
}
424+
425+
if erlang {
426+
external fn exec_(String, Connection) -> Result(Nil, Error) =
427+
"sqlight_ffi" "exec"
428+
}
429+
430+
if javascript {
431+
external fn exec_(String, Connection) -> Result(Nil, Error) =
432+
"./sqlight_ffi.js" "exec"
433+
}
436434

437435
/// Convert a Gleam `Int` to an SQLite int, to be used an argument to a
438436
/// query.
439437
///
440-
pub external fn int(Int) -> Value =
441-
"sqlight_ffi" "coerce_value"
438+
pub fn int(value: Int) -> Value {
439+
coerce_value(value)
440+
}
442441

443442
/// Convert a Gleam `Float` to an SQLite float, to be used an argument to a
444443
/// query.
445444
///
446-
pub external fn float(Float) -> Value =
447-
"sqlight_ffi" "coerce_value"
445+
pub fn float(value: Float) -> Value {
446+
coerce_value(value)
447+
}
448448

449449
/// Convert a Gleam `String` to an SQLite text, to be used an argument to a
450450
/// query.
451451
///
452-
pub external fn text(String) -> Value =
453-
"sqlight_ffi" "coerce_value"
452+
pub fn text(value: String) -> Value {
453+
coerce_value(value)
454+
}
454455

455456
/// Convert a Gleam `BitString` to an SQLite blob, to be used an argument to a
456457
/// query.
457458
///
458-
pub external fn blob(BitString) -> Value =
459-
"sqlight_ffi" "coerce_value"
459+
pub fn blob(value: BitString) -> Value {
460+
coerce_blob(value)
461+
}
462+
463+
if erlang {
464+
external fn coerce_blob(BitString) -> Value =
465+
"sqlight_ffi" "coerce_value"
466+
}
467+
468+
if javascript {
469+
external fn coerce_blob(BitString) -> Value =
470+
"./sqlight_ffi.js" "coerce_blob"
471+
}
460472

461473
/// Convert a Gleam `Bool` to an SQLite int, to be used an argument to a
462474
/// query.
@@ -473,10 +485,19 @@ pub fn bool(value: Bool) -> Value {
473485
})
474486
}
475487

476-
/// Construct an SQLite null, to be used an argument to a query.
477-
///
478-
pub external fn null() -> Value =
479-
"sqlight_ffi" "null"
488+
if erlang {
489+
/// Construct an SQLite null, to be used an argument to a query.
490+
///
491+
pub external fn null() -> Value =
492+
"sqlight_ffi" "null"
493+
}
494+
495+
if javascript {
496+
/// Construct an SQLite null, to be used an argument to a query.
497+
///
498+
pub external fn null() -> Value =
499+
"./sqlight_ffi.js" "null_"
500+
}
480501

481502
pub fn decode_bool(value: Dynamic) -> Result(Bool, List(dynamic.DecodeError)) {
482503
case dynamic.int(value) {
@@ -486,20 +507,6 @@ pub fn decode_bool(value: Dynamic) -> Result(Bool, List(dynamic.DecodeError)) {
486507
}
487508
}
488509

489-
/// Return a description of the last occurred error.
490-
///
491-
external fn error_info(Connection) -> #(String, Int) =
492-
"sqlight_ffi" "error_info"
493-
494-
fn construct_error(connection: Connection, error_code: Int) -> Error {
495-
let #(message, offset) = error_info(connection)
496-
SqlightError(
497-
code: error_code_from_int(error_code),
498-
message: message,
499-
offset: offset,
500-
)
501-
}
502-
503510
fn decode_error(errors: List(dynamic.DecodeError)) -> Error {
504511
assert [dynamic.DecodeError(expected, actual, path), ..] = errors
505512
let path = string.join(path, ".")

src/sqlight_ffi.erl

+24-13
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
11
-module(sqlight_ffi).
22

33
-export([
4-
normalise_result/1, status/0, query/3, exec/2, coerce_value/1, null/0,
5-
error_info/1
4+
status/0, query/3, exec/2, coerce_value/1, null/0, open/1, close/1
65
]).
76

7+
open(Name) ->
8+
case esqlite3:open(unicode:characters_to_list(Name)) of
9+
{ok, Connection} -> {ok, Connection};
10+
{error, Code} ->
11+
Code1 = sqlight:error_code_from_int(Code),
12+
{error, {sqlight_error, Code1, <<>>, -1}}
13+
end.
14+
15+
close(Connection) ->
16+
case esqlite3:close(Connection) of
17+
ok -> {ok, nil};
18+
{error, Code} -> to_error(Connection, Code)
19+
end.
20+
821
query(Sql, Connection, Arguments) ->
922
case esqlite3:q(Sql, Connection, Arguments) of
10-
{error, Code} when is_integer(Code) -> {error, Code};
23+
{error, Code} ->
24+
to_error(Connection, Code);
1125
Rows -> {ok, lists:map(fun erlang:list_to_tuple/1, Rows)}
1226
end.
1327

1428
exec(Sql, Connection) ->
15-
case esqlite3:exec(Sql, Connection) of
16-
{error, Code} when is_integer(Code) -> {error, Code};
29+
case esqlite3:exec(Connection, Sql) of
30+
{error, Code} -> to_error(Connection, Code);
1731
ok -> {ok, nil}
1832
end.
1933

20-
normalise_result(Result) ->
21-
case Result of
22-
ok -> {ok, nil};
23-
{error, Code} when is_integer(Code) -> {error, Code}
24-
end.
25-
2634
stats(#{used := Used, highwater := Highwater}) ->
2735
{stats, Used, Highwater}.
2836

@@ -49,6 +57,9 @@ status() ->
4957
coerce_value(X) -> X.
5058
null() -> undefined.
5159

52-
error_info(Connection) ->
60+
to_error(Connection = {esqlite3, _}, Code) when is_integer(Code) ->
5361
#{errmsg := Message, error_offset := Offset} = esqlite3:error_info(Connection),
54-
{Message, Offset}.
62+
Error = {sqlight_error, sqlight:error_code_from_int(Code), Message, Offset},
63+
{error, Error};
64+
to_error(Connection, Code) ->
65+
erlang:throw({fail, Connection, Code}).

0 commit comments

Comments
 (0)