lib/basic_ecommerce.ex

defmodule BasicECommerce do
  @moduledoc """
  Main module for this library.

  To use it, it needs to be used on your own application and it should configure an adapter
  by specifying the used `otp_app` and correctly configuring the adapter for this OTP app.

  ## Example
      # config/config.exs
      config :my_app, MyApp.BasicECommerce,
        adapter: BasicECommerce.DataSource.JsonAdapter,
        json_file_path: '/data/pricing_data.json`

      # my_app/basic_ecommerce.ex
      defmodule MyApp.BasicECommerce do
        use BasicECommerce, otp_app: :my_app
      end
  """

  defmacro __using__(opts) do
    quote bind_quoted: [opts: opts] do
      @otp_app Keyword.fetch!(opts, :otp_app)
      @config Application.get_env(@otp_app, __MODULE__, [])
      @adapter Keyword.fetch!(@config, :adapter)

      @doc """
      Returns the list of all products in the data source.
      """
      @spec list_products() :: [BasicECommerce.Product.t()]
      def list_products do
        @adapter.list_products(@config)
      end

      @doc """
      Gets a single product by its SKU.
      """
      @spec get_product(sku :: atom()) :: {:ok, BasicECommerce.Product.t()} | {:error, String.t()}
      def get_product(sku) do
        @adapter.get_product(sku, @config)
      end

      @doc """
      Returns the applicable special offers for a given SKU.
      """
      @spec applicable_special_offers(sku :: atom()) :: [BasicECommerce.SpecialOffer.t()]
      def applicable_special_offers(sku) do
        @adapter.applicable_special_offers(sku, @config)
      end

      @doc """
      Compute and return the total price of an order
      """
      @spec compute_total(order :: BasicECommerce.Order.t()) :: integer()
      def compute_total(order) do
        BasicECommerce.Order.total!(order, &get_product/1, &applicable_special_offers/1)
      end

      @on_load :validate_config

      @doc false
      def validate_config do
        @adapter.validate_config(@config)
      end
    end
  end

end