src/erupt_sqlite3.erl

-module(erupt_sqlite3).

-behaviour(erupt_driver).

-export([up/1, down/1, list/1, add/3, last/1, delete/2, exec/2]).

-spec exec(any(), iodata()) -> ok | {error, any()}.
exec(Conn, Query) ->
    case esqlite3:q(Conn, Query, []) of
        {error, _Err} = E ->
            E;
        _ ->
            ok
    end.

-spec up(any()) -> ok | {error, any()}.
up(Conn) ->
    case esqlite3:q(Conn,
                    [<<"CREATE TABLE IF NOT EXISTS __erupt_migrations ( ">>,
                     <<"  id INTEGER PRIMARY KEY AUTOINCREMENT, ">>,
                     <<"  name TEXT NOT NULL, ">>,
                     <<"  timestamp INTEGER NOT NULL ">>,
                     <<")">>],
                    [])
    of
        {error, _Err} = E ->
            E;
        _ ->
            ok
    end.

-spec down(any()) -> ok | {error, any()}.
down(Conn) ->
    case esqlite3:q(Conn, <<"DROP TABLE __erupt_migrations">>, []) of
        {error, _Err} = E ->
            E;
        _ ->
            ok
    end.

-spec list(any()) -> {ok, list()} | {error, any()}.
list(Conn) ->
    case esqlite3:q(Conn,
                    [<<"SELECT id, name, timestamp ">>,
                     <<"FROM __erupt_migrations ">>,
                     <<"ORDER BY timestamp ASC">>],
                    [])
    of
        {error, _Err} = E ->
            E;
        Rows ->
            {ok, format_rows(Rows)}
    end.

-spec format_rows(list()) -> list() | {error, invalid_row}.
format_rows(Rows) ->
    lists:map(fun format_row/1, Rows).

-spec format_row(list() | any()) ->
                    {integer(), module(), integer()} | {error, invalid_row}.
format_row([Id, Name, Timestamp]) ->
    {Id, binary_to_atom(Name), Timestamp};
format_row(_) ->
    {error, invalid_row}.

-spec add(any(), binary(), integer()) -> ok | {error, any()}.
add(Conn, Name, Timestamp) ->
    case esqlite3:q(Conn,
                    <<"INSERT INTO __erupt_migrations (name, timestamp) VALUES (?, ?)">>,
                    [Name, Timestamp])
    of
        {error, _Err} = E ->
            E;
        _ ->
            ok
    end.

-spec last(any()) -> {error, any()} | {ok, {integer(), module(), integer()}}.
last(Conn) ->
    case esqlite3:q(Conn,
                    [<<"SELECT id, name, timestamp ">>,
                     <<"FROM __erupt_migrations ">>,
                     <<"ORDER BY timestamp DESC ">>,
                     <<"LIMIT 1">>],
                    [])
    of
        {error, _Err} = E ->
            E;
        [[Id, Name, Timestamp]] ->
            {ok, {Id, binary_to_atom(Name), Timestamp}}
    end.

-spec delete(any(), integer()) -> ok | {error, any()}.
delete(Conn, Id) ->
    case esqlite3:q(Conn, <<"DELETE FROM __erupt_migrations WHERE id = ?">>, [Id]) of
        {error, _Err} = E ->
            E;
        _ ->
            ok
    end.