Current section
Files
Jump to
Current section
Files
src/gorrion.erl
-module(gorrion).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gorrion.gleam").
-export([migrate/2, rollback/2, rollback_to/3, status/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Gorrion — Ecto-like database migration library for Gleam.\n"
"\n"
" Reads migration SQL from .sql files on disk, tracks applied migrations\n"
" in a _schema_migrations table, and supports forward migration and rollback.\n"
"\n"
" ## Usage\n"
"\n"
" ```gleam\n"
" // Run all pending migrations\n"
" gorrion.migrate(db, \"migrations\")\n"
"\n"
" // Roll back the most recent migration\n"
" gorrion.rollback(db, \"migrations\")\n"
"\n"
" // Check migration status\n"
" gorrion.status(db, \"migrations\")\n"
" ```\n"
).
-file("src/gorrion.gleam", 137).
?DOC(
" Internal: ensure tracking table exists, get applied migrations,\n"
" and compute which migrations are pending.\n"
).
-spec resolve(pog:connection(), list(gorrion@types:migration())) -> {ok,
{list(gorrion@types:migration()),
list(gorrion@types:applied_migration())}} |
{error, gorrion@types:migration_error()}.
resolve(Db, Migrations) ->
gleam@result:'try'(
gorrion@tracker:ensure_table(Db),
fun(_) ->
gleam@result:'try'(
gorrion@tracker:get_applied(Db),
fun(Applied) ->
Applied_versions = begin
_pipe = Applied,
_pipe@1 = gleam@list:map(
_pipe,
fun(A) -> erlang:element(2, A) end
),
gleam@set:from_list(_pipe@1)
end,
Pending = begin
_pipe@2 = Migrations,
_pipe@3 = gleam@list:filter(
_pipe@2,
fun(M) ->
not gleam@set:contains(
Applied_versions,
erlang:element(2, M)
)
end
),
gleam@list:sort(
_pipe@3,
fun(A@1, B) ->
gleam@int:compare(
erlang:element(2, A@1),
erlang:element(2, B)
)
end
)
end,
{ok, {Pending, Applied}}
end
)
end
).
-file("src/gorrion.gleam", 33).
?DOC(" Run all pending migrations in version order.\n").
-spec migrate(pog:connection(), binary()) -> {ok, nil} |
{error, gorrion@types:migration_error()}.
migrate(Db, Dir) ->
gleam@result:'try'(
gorrion@loader:load_migrations(Dir),
fun(Migrations) ->
gleam@result:'try'(
resolve(Db, Migrations),
fun(_use0) ->
{Pending, _} = _use0,
case Pending of
[] ->
gleam_stdlib:println(
<<"No pending migrations"/utf8>>
),
{ok, nil};
_ ->
gleam_stdlib:println(
<<<<"Applying "/utf8,
(erlang:integer_to_binary(
erlang:length(Pending)
))/binary>>/binary,
" pending migration(s)..."/utf8>>
),
gleam@list:try_fold(
Pending,
nil,
fun(_, M) ->
gorrion@runner:apply_migration(Db, M)
end
)
end
end
)
end
).
-file("src/gorrion.gleam", 57).
?DOC(" Roll back the most recently applied migration.\n").
-spec rollback(pog:connection(), binary()) -> {ok, nil} |
{error, gorrion@types:migration_error()}.
rollback(Db, Dir) ->
gleam@result:'try'(
gorrion@loader:load_migrations(Dir),
fun(Migrations) ->
gleam@result:'try'(
resolve(Db, Migrations),
fun(_use0) ->
{_, Applied} = _use0,
Applied_versions = begin
_pipe = Applied,
_pipe@1 = gleam@list:map(
_pipe,
fun(A) -> erlang:element(2, A) end
),
gleam@set:from_list(_pipe@1)
end,
Latest = begin
_pipe@2 = Migrations,
_pipe@3 = gleam@list:filter(
_pipe@2,
fun(M) ->
gleam@set:contains(
Applied_versions,
erlang:element(2, M)
)
end
),
_pipe@4 = gleam@list:sort(
_pipe@3,
fun(A@1, B) ->
gleam@int:compare(
erlang:element(2, B),
erlang:element(2, A@1)
)
end
),
gleam@list:first(_pipe@4)
end,
case Latest of
{error, _} ->
{error, no_migrations_to_rollback};
{ok, Migration} ->
gleam_stdlib:println(
<<"Rolling back 1 migration..."/utf8>>
),
gorrion@runner:revert_migration(Db, Migration)
end
end
)
end
).
-file("src/gorrion.gleam", 86).
?DOC(" Roll back all migrations down to (but not including) the target version.\n").
-spec rollback_to(pog:connection(), binary(), integer()) -> {ok, nil} |
{error, gorrion@types:migration_error()}.
rollback_to(Db, Dir, Target) ->
gleam@result:'try'(
gorrion@loader:load_migrations(Dir),
fun(Migrations) ->
gleam@result:'try'(
resolve(Db, Migrations),
fun(_use0) ->
{_, Applied} = _use0,
Applied_versions = begin
_pipe = Applied,
_pipe@1 = gleam@list:map(
_pipe,
fun(A) -> erlang:element(2, A) end
),
gleam@set:from_list(_pipe@1)
end,
To_revert = begin
_pipe@2 = Migrations,
_pipe@3 = gleam@list:filter(
_pipe@2,
fun(M) ->
gleam@set:contains(
Applied_versions,
erlang:element(2, M)
)
andalso (erlang:element(2, M) > Target)
end
),
gleam@list:sort(
_pipe@3,
fun(A@1, B) ->
gleam@int:compare(
erlang:element(2, B),
erlang:element(2, A@1)
)
end
)
end,
case To_revert of
[] ->
gleam_stdlib:println(
<<"No migrations to roll back"/utf8>>
),
{ok, nil};
_ ->
gleam_stdlib:println(
<<<<"Rolling back "/utf8,
(erlang:integer_to_binary(
erlang:length(To_revert)
))/binary>>/binary,
" migration(s)..."/utf8>>
),
gleam@list:try_fold(
To_revert,
nil,
fun(_, M@1) ->
gorrion@runner:revert_migration(Db, M@1)
end
)
end
end
)
end
).
-file("src/gorrion.gleam", 126).
?DOC(" Get the current migration status: which are applied and which are pending.\n").
-spec status(pog:connection(), binary()) -> {ok,
gorrion@types:migration_status()} |
{error, gorrion@types:migration_error()}.
status(Db, Dir) ->
gleam@result:'try'(
gorrion@loader:load_migrations(Dir),
fun(Migrations) ->
gleam@result:'try'(
resolve(Db, Migrations),
fun(_use0) ->
{Pending, Applied} = _use0,
{ok, {migration_status, Applied, Pending}}
end
)
end
).