defmodule Fsdb.Server do @moduledoc """ Fsdb.Server module. ```elixir #run with: mix sample_server path = Path.expand("_build/dev/.fsdb") {:ok, db} = Fsdb.Server.start_link([path: path]) #drop to start over :ok = Fsdb.Server.drop(db, "table1") :ok = Fsdb.Server.create(db, "table1") #insert generates and returns an autoincrementing id {:ok, 1} = Fsdb.Server.insert(db, "table1", "row1") {:ok, "row1"} = Fsdb.Server.fetch(db, "table1", 1) #operation on nonexisting ids return not found :nf = Fsdb.Server.delete(db, "table1", 5) :nf = Fsdb.Server.update(db, "table1", 5, "row5+") #save overrides and updates id generator :ok = Fsdb.Server.save(db, "table1", 5, "row5") :ok = Fsdb.Server.save(db, "table1", 3, "row3") #continue +1 of largest id used/generated before {:ok, 6} = Fsdb.Server.insert(db, "table1", "row6") #operations that get back previous value {:ok, "row5"} = Fsdb.Server.update(db, "table1", 5, "row5+") {:ok, "row5+"} = Fsdb.Server.save(db, "table1", 5, "row5++") {:ok, "row5++"} = Fsdb.Server.delete(db, "table1", 5) #tuples may not be id ordered [{1, "row1"}, {3, "row3"}, {6, "row6"}] = Fsdb.Server.list(db, "table1") ``` """ use GenServer ########################################## # Public API ########################################## @doc """ Starts the GenServer. `params` *must* contain a valid path in the `:path` key. `opts` is optional and is passed verbatim to GenServer. Returns `{:ok, pid}`. ## Example ``` Fsdb.Server.start_link([path: ".fsdb"], [name: Fsdb.Server]) ``` """ def start_link(params, opts \\ []) do GenServer.start_link(__MODULE__, params, opts) end @doc """ Stops the GenServer. Returns `:ok`. """ def stop(pid) do GenServer.stop(pid) end @doc """ Creates a table with name `table`. Returns `:ok`. """ def create(pid, table) do GenServer.call(pid, {:create, table}) end @doc """ Drops the table named `table`. Returns `:ok`. """ def drop(pid, table) do GenServer.call(pid, {:drop, table}) end @doc """ Returns all rows in table `table`. Returns `[{id, row}, ...]`. """ def list(pid, table) do GenServer.call(pid, {:list, table}) end @doc """ Inserts row `row` in table `table` with auto generated id. Returns `{:ok, id}`. """ def insert(pid, table, row) do GenServer.call(pid, {:insert, table, row}) end @doc """ Inserts or updates row `row` in table `table` with specific id `id`. If the provided id is greater than the last autogenerated id then the autogenerated id is updated to the provided one. Returns `:ok | {:ok, previous_row}`. """ def save(pid, table, id, row) when is_integer(id) do GenServer.call(pid, {:save, table, id, row}) end @doc """ Fetches the row in table `table` having the id `id`. Returns `{:ok, row} | :nf`. """ def fetch(pid, table, id) do GenServer.call(pid, {:fetch, table, id}) end @doc """ Updates the row in table `table` having the id `id`. Returns `{:ok, previous_row} | :nf`. """ def update(pid, table, id, row) do GenServer.call(pid, {:update, table, id, row}) end @doc """ Deletes the row in table `table` having the id `id`. Returns `{:ok, previous_row} | :nf`. """ def delete(pid, table, id) do GenServer.call(pid, {:delete, table, id}) end ########################################## # GenServer Implementation ########################################## def init(params) do path = Keyword.fetch!(params, :path) Fsdb.init(path) {:ok, path} end def handle_call({:create, table}, _from, path) do {:reply, Fsdb.create(path, table), path} end def handle_call({:drop, table}, _from, path) do {:reply, Fsdb.drop(path, table), path} end def handle_call({:list, table}, _from, path) do {:reply, Fsdb.list(path, table), path} end def handle_call({:insert, table, row}, _from, path) do {:reply, Fsdb.insert(path, table, row), path} end def handle_call({:save, table, id, row}, _from, path) do {:reply, Fsdb.save(path, table, id, row), path} end def handle_call({:fetch, table, id}, _from, path) do {:reply, Fsdb.fetch(path, table, id), path} end def handle_call({:update, table, id, row}, _from, path) do {:reply, Fsdb.update(path, table, id, row), path} end def handle_call({:delete, table, id}, _from, path) do {:reply, Fsdb.delete(path, table, id), path} end end