-module(refrakt@migrate). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/refrakt/migrate.gleam"). -export([run_from_directory/3]). -export_type([migration/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type migration() :: {migration, integer(), binary(), binary(), binary()}. -file("src/refrakt/migrate.gleam", 79). -spec parse_migration(binary(), binary()) -> {ok, migration()} | {error, nil}. parse_migration(Dir, Filename) -> Parts = gleam@string:split(Filename, <<"_"/utf8>>), case Parts of [Num_str | Rest] -> case gleam_stdlib:parse_int(Num_str) of {ok, Num} -> Name = begin _pipe = Rest, _pipe@1 = gleam@string:join(_pipe, <<"_"/utf8>>), gleam@string:replace( _pipe@1, <<".sql"/utf8>>, <<""/utf8>> ) end, case simplifile:read( <<<>/binary, Filename/binary>> ) of {ok, Sql} -> {ok, {migration, Num, Name, Filename, Sql}}; {error, _} -> {error, nil} end; {error, _} -> {error, nil} end; _ -> {error, nil} end. -file("src/refrakt/migrate.gleam", 107). -spec apply_migrations( list(migration()), fun((binary()) -> {ok, nil} | {error, binary()}) ) -> {ok, nil} | {error, binary()}. apply_migrations(Migrations, Db_execute) -> case Migrations of [] -> {ok, nil}; [Migration | Rest] -> gleam_stdlib:println( <<<<" "/utf8, (erlang:element(4, Migration))/binary>>/binary, " ..."/utf8>> ), gleam@result:'try'( begin _pipe = Db_execute(erlang:element(5, Migration)), gleam@result:map_error( _pipe, fun(E) -> <<<<<<"Migration "/utf8, (erlang:element(4, Migration))/binary>>/binary, " failed: "/utf8>>/binary, E/binary>> end ) end, fun(_) -> gleam@result:'try'( Db_execute( <<<<"INSERT INTO _migrations (name) VALUES ('"/utf8, (erlang:element(4, Migration))/binary>>/binary, "')"/utf8>> ), fun(_) -> gleam_stdlib:println( <<<<" "/utf8, (erlang:element(4, Migration))/binary>>/binary, " ✓"/utf8>> ), apply_migrations(Rest, Db_execute) end ) end ) end. -file("src/refrakt/migrate.gleam", 33). ?DOC( " Run all pending migrations in a directory.\n" " Returns the number of migrations applied.\n" ). -spec run_from_directory( fun((binary()) -> {ok, nil} | {error, binary()}), fun((binary()) -> {ok, list(binary())} | {error, binary()}), binary() ) -> {ok, integer()} | {error, binary()}. run_from_directory(Db_execute, Db_query_strings, Migrations_dir) -> gleam@result:'try'( Db_execute( <<"CREATE TABLE IF NOT EXISTS _migrations ( name TEXT PRIMARY KEY, applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW() )"/utf8>> ), fun(_) -> gleam@result:'try'( Db_query_strings( <<"SELECT name FROM _migrations ORDER BY name"/utf8>> ), fun(Applied) -> gleam@result:'try'( begin _pipe = simplifile_erl:read_directory( Migrations_dir ), gleam@result:replace_error( _pipe, <<"Could not read migrations directory: "/utf8, Migrations_dir/binary>> ) end, fun(Files) -> Migrations = begin _pipe@1 = Files, _pipe@2 = gleam@list:filter( _pipe@1, fun(F) -> gleam_stdlib:string_ends_with( F, <<".sql"/utf8>> ) end ), _pipe@3 = gleam@list:filter_map( _pipe@2, fun(Filename) -> parse_migration( Migrations_dir, Filename ) end ), gleam@list:sort( _pipe@3, fun(A, B) -> gleam@int:compare( erlang:element(2, A), erlang:element(2, B) ) end ) end, Pending = begin _pipe@4 = Migrations, gleam@list:filter( _pipe@4, fun(M) -> not gleam@list:contains( Applied, erlang:element(4, M) ) end ) end, gleam@result:'try'( apply_migrations(Pending, Db_execute), fun(_) -> {ok, erlang:length(Pending)} end ) end ) end ) end ).