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