Skip to main content

lib/webql.ex

defmodule Webql do
  @moduledoc """
  Defines and runs WebQL instances.

  ## Examples

      defmodule MyApp.Webql do
        use Webql,
          memory: MyApp.Webql.Memory.new/0,
          engine: MyApp.Webql.Engine.new/0
      end
  """
  @moduledoc version: "0.1.0"

  @doc """
  Creates a new WebQL instance.
  """
  @callback new(memory :: term(), engine :: term()) :: term()

  @doc """
  Runs an operation with the given parameters.
  """
  @callback run(webql :: term(), source :: binary(), dsl :: module(), params :: map()) :: map()

  @doc """
  Returns introspection results for a WebQL schema.
  """
  @callback introspect(dsl :: module()) :: map()

  @doc """
  Returns the configured memory instance.
  """
  @callback __memory__() :: term()

  @doc """
  Returns the configured engine instance.
  """
  @callback __engine__() :: term()

  defmacro __using__(opts) do
    quote location: :keep do
      @behaviour Webql

      @impl Webql
      def new(memory \\ __memory__(), engine \\ __engine__()) do
        Webql.new(memory, engine)
      end

      @impl Webql
      def run(webql \\ new(), source, dsl, params) do
        Webql.run(webql, source, dsl, params)
      end

      @impl Webql
      def introspect(dsl) do
        Webql.introspect(dsl)
      end

      @impl Webql
      def __memory__, do: Keyword.get(unquote(opts), :memory, Webql.__memory__())

      @impl Webql
      def __engine__, do: Keyword.get(unquote(opts), :engine, Webql.__engine__())

      defoverridable new: 2, run: 3, introspect: 1
    end
  end

  @doc false
  def new(memory, engine) when is_tuple(memory) and is_tuple(engine) do
    :webql.new(memory, engine)
  end

  @doc false
  def run(webql, source, dsl, params)
      when is_tuple(webql) and is_binary(source) and is_atom(dsl) and is_map(params) do
    schema = Webql.Schema.Builder.build(dsl)
    :webql.run(webql, source, schema, params)
  end

  @doc false
  def introspect(dsl) when is_atom(dsl) do
    schema = Webql.Schema.Builder.build(dsl)
    :webql.introspect(schema)
  end

  @doc false
  def __memory__(), do: :webql@memory.new()

  @doc false
  def __engine__(), do: :webql@engine@basic.new()
end