defmodule ExOAPI do @moduledoc """ The module containing the high-level functionality for generating SDKs from an OpenAPI V3 specification. """ @spec generate(%{ required(:source) => String.t(), required(:output_path) => String.t(), optional(:output_type) => :app | :modules, optional(:parser) => any(), optional(:generator) => any() }) :: {:ok, :generated} | {:error, term()} def generate(opts) do with source <- Map.get(opts, :source), {_, {:ok, output}} <- {:output, Map.fetch(opts, :output_path)}, output_type <- Map.get(opts, :output_type, :modules), title <- Map.get(opts, :title), parse_config <- Map.get(opts, :parser, %{}), gen_config <- Map.get(opts, :generator, %{}), gen_config <- Map.put(gen_config, :output_type, output_type), gen_config <- Map.put(gen_config, :output_path, output), gen_config <- Map.put(gen_config, :title, title), {_, :ok} <- {:source, verify_file(source)} do parse_and_create(source, parse_config, gen_config) else {:source, error} -> error {:output, _} -> {:error, :output_not_specified} end end defp parse_and_create(source, parse_config, gen_config) do with {:ok, ctx} <- parse(source, parse_config), {:ok, _} <- create(ctx, gen_config) do {:ok, :generated} end end defp parse(source, config) do with {_, {:ok, file}} <- {:read_source, File.read(source)}, {_, {:ok, parsed}} <- {:json_parse, Jason.decode(file)} do config |> ExOAPI.Parser.V3.Context.new() |> ExOAPI.Parser.V3.Context.map_cast(parsed) |> ExOAPI.Parser.V3.Context.maybe_add_skipped_schemas() |> ExOAPI.Parser.V3.Context.normalize_all_ofs() end end defp create(%ExOAPI.Parser.V3.Context{} = ctx, %{output_type: :app} = config), do: ExOAPI.Generator.generate_app(ctx, config) defp create(%ExOAPI.Parser.V3.Context{} = ctx, config), do: ExOAPI.Generator.generate_templates(ctx, config) defp verify_file(file_path) do with {_, true} <- {:exists?, File.exists?(file_path)}, {_, false} <- {:dir?, File.dir?(file_path)} do :ok else {:exists?, _} -> {:error, {file_path, :enoent}} {:dir?, _} -> {:error, {file_path, :eisdir}} end end end