Current section

Files

Jump to
storch src storch.erl
Raw

src/storch.erl

-module(storch).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([migrate/2, get_migrations/1]).
-export_type([migration/0, migration_error/0]).
-type migration() :: {migration, integer(), binary()}.
-type migration_error() :: {directory_not_exist, binary()} |
{invalid_migration_name, binary()} |
{invalid_migration_id, binary()} |
{transaction_error, sqlight:error()} |
{migrations_table_error, sqlight:error()} |
{migration_script_error, integer(), sqlight:error()}.
-spec migration_error(migration_error()) -> fun((any()) -> migration_error()).
migration_error(Error) ->
fun(_) -> Error end.
-spec migrate(list(migration()), sqlight:connection()) -> {ok, nil} |
{error, migration_error()}.
migrate(Migrations, Connection) ->
Transaction = (gleam@result:'try'(
begin
_pipe = sqlight:exec(<<"begin transaction;"/utf8>>, Connection),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {transaction_error, Field@0} end
)
end,
fun(_) ->
gleam@result:'try'(
begin
_pipe@1 = sqlight:exec(
<<"create table if not exists storch_migrations (id integer, applied integer);"/utf8>>,
Connection
),
gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {migrations_table_error, Field@0} end
)
end,
fun(_) ->
Migrations_decoder = gleam@dynamic:tuple2(
fun gleam@dynamic:int/1,
fun sqlight:decode_bool/1
),
Applications = gleam@list:try_each(
Migrations,
fun(Migration) ->
gleam@result:'try'(
begin
_pipe@2 = sqlight:'query'(
<<"select id, applied from storch_migrations where id = ?;"/utf8>>,
Connection,
[sqlight:int(
erlang:element(2, Migration)
)],
Migrations_decoder
),
gleam@result:map_error(
_pipe@2,
fun(Field@0) -> {migrations_table_error, Field@0} end
)
end,
fun(Migrated) ->
Already_applied = case Migrated of
[] ->
false;
[{_, Applied}] ->
Applied;
_ ->
erlang:error(#{gleam_error => panic,
message => <<"Multiple migrations with the same id in the storch migrations table"/utf8>>,
module => <<"storch"/utf8>>,
function => <<"migrate"/utf8>>,
line => 78})
end,
gleam@bool:guard(
Already_applied,
{ok, nil},
fun() ->
gleam@result:'try'(
begin
_pipe@3 = sqlight:exec(
erlang:element(
3,
Migration
),
Connection
),
gleam@result:map_error(
_pipe@3,
fun(_capture) ->
{migration_script_error,
erlang:element(
2,
Migration
),
_capture}
end
)
end,
fun(_) ->
gleam@result:'try'(
begin
_pipe@4 = sqlight:'query'(
<<"insert into storch_migrations (id, applied) values (?,?) returning *;"/utf8>>,
Connection,
[sqlight:int(
erlang:element(
2,
Migration
)
),
sqlight:bool(
true
)],
Migrations_decoder
),
gleam@result:map_error(
_pipe@4,
fun(Field@0) -> {migrations_table_error, Field@0} end
)
end,
fun(_) -> {ok, nil} end
)
end
)
end
)
end
)
end
),
gleam@result:'try'(
Applications,
fun(_) ->
gleam@result:'try'(
begin
_pipe@5 = sqlight:exec(
<<"commit;"/utf8>>,
Connection
),
gleam@result:map_error(
_pipe@5,
fun(Field@0) -> {transaction_error, Field@0} end
)
end,
fun(_) -> {ok, nil} end
)
end
)
end
)
end
)),
case Transaction of
{ok, _} ->
{ok, nil};
{error, Err} ->
gleam@io:println(<<"error running migration"/utf8>>),
gleam@io:debug(Err),
gleam@io:println(<<"rolling back"/utf8>>),
_ = sqlight:exec(<<"rollback;"/utf8>>, Connection),
{error, Err}
end.
-spec read_migrations(list(binary())) -> {ok, list({integer(), binary()})} |
{error, migration_error()}.
read_migrations(Paths) ->
gleam@list:try_map(
Paths,
fun(Path) ->
Filename = filepath:base_name(Path),
gleam@result:'try'(
begin
_pipe = gleam@string:split_once(Filename, <<"_"/utf8>>),
gleam@result:map_error(
_pipe,
migration_error({invalid_migration_name, Filename})
)
end,
fun(_use0) ->
{Id, _} = _use0,
gleam@result:'try'(
begin
_pipe@1 = gleam@int:parse(Id),
gleam@result:map_error(
_pipe@1,
migration_error({invalid_migration_id, Id})
)
end,
fun(Id@1) ->
_assert_subject = simplifile:read(Path),
{ok, Contents} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"storch"/utf8>>,
function => <<"read_migrations"/utf8>>,
line => 152})
end,
{ok, {Id@1, Contents}}
end
)
end
)
end
).
-spec get_migration_filenames(binary()) -> {ok, list(binary())} |
{error, migration_error()}.
get_migration_filenames(Directory) ->
gleam@result:'try'(
begin
_pipe = simplifile:is_directory(Directory),
gleam@result:map_error(
_pipe,
migration_error({directory_not_exist, Directory})
)
end,
fun(Is_dir) ->
gleam@bool:guard(
not Is_dir,
{error, {directory_not_exist, Directory}},
fun() ->
gleam@result:'try'(
begin
_pipe@1 = simplifile:get_files(Directory),
gleam@result:map_error(
_pipe@1,
migration_error(
{directory_not_exist, Directory}
)
)
end,
fun(Filenames_raw) ->
_pipe@4 = gleam@list:map(
Filenames_raw,
fun(Path) ->
gleam@result:'try'(
filepath:extension(Path),
fun(Extension) ->
Base_path = filepath:directory_name(
Path
),
Filename = begin
_pipe@2 = filepath:base_name(
Path
),
filepath:strip_extension(
_pipe@2
)
end,
gleam@bool:guard(
Extension /= <<"sql"/utf8>>,
{error, nil},
fun() ->
gleam@bool:guard(
Base_path /= Directory,
{error, nil},
fun() ->
gleam@result:'try'(
gleam@string:split_once(
Filename,
<<"_"/utf8>>
),
fun(_use0) ->
{Numbers, _} = _use0,
gleam@result:'try'(
begin
_pipe@3 = gleam@regex:from_string(
<<"^[0-9]+$"/utf8>>
),
gleam@result:nil_error(
_pipe@3
)
end,
fun(
Regex
) ->
gleam@bool:guard(
not gleam@regex:check(
Regex,
Numbers
),
{error,
nil},
fun(
) ->
{ok,
Path}
end
)
end
)
end
)
end
)
end
)
end
)
end
),
_pipe@5 = gleam@result:values(_pipe@4),
{ok, _pipe@5}
end
)
end
)
end
).
-spec get_migrations(binary()) -> {ok, list(migration())} |
{error, migration_error()}.
get_migrations(Directory) ->
gleam@result:'try'(
get_migration_filenames(Directory),
fun(Filenames) ->
gleam@result:'try'(
read_migrations(Filenames),
fun(Raw_migrations) ->
_pipe = gleam@list:map(
Raw_migrations,
fun(Raw) ->
{migration,
erlang:element(1, Raw),
erlang:element(2, Raw)}
end
),
_pipe@1 = gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@int:compare(
erlang:element(2, A),
erlang:element(2, B)
)
end
),
{ok, _pipe@1}
end
)
end
).