defmodule Mix.Tasks.BuildSdk do @moduledoc false use Mix.Task def run(_) do build_sdk() end def build_sdk() do {_, _, :elixir, _, %{"en" => _module_doc}, _, docs} = module() |> Code.fetch_docs() fxns = docs |> Enum.map_join(fn {{:function, name_as_atom, arity}, _, [_signature], :none, %{}} -> raise("missing docs for #{name_as_atom}/#{arity} inside #{to_string(module())}") {{:function, name_as_atom, arity}, _, [signature], %{"en" => fxn_doc}, %{}} -> spacer = if arity == 0, do: "", else: ", " new_signature = signature |> String.replace_trailing(")", "#{spacer}method \\\\ :call)") spec = get_spec(name_as_atom, arity) args = new_signature |> String.split("(") |> List.last() |> String.split(" \\") |> List.first() """ \t@doc \"\"\" \t#{fxn_doc}\t\"\"\" \t#{spec} \tdef #{new_signature} when method in [:call, :cast], do: go(method, :#{name_as_atom}, [#{args}]) """ _ -> "" end) file_name = ("sdk/" <> to_string(module(:simple)) <> ".ex") |> String.downcase() File.write!( file_name, """ defmodule InternalServices.#{module(:simple)} do # [This module has been automatically generated and should not be modified.] @moduledoc \"\"\" SDK for #{module(:simple)}. \"\"\" @prefix \"#{to_string(app_name())}\" @module #{module(:simple)} #{fxns} defp go(method, fxn, args), do: InternalServices.Caller.go(method, @prefix, @module, fxn, args) end """ |> Code.format_string!() ) end defp get_spec(name, arity) do with {:ok, module_specs} <- Code.Typespec.fetch_specs(module()), {_, function_specs} <- List.keyfind(module_specs, {name, arity}, 0) do Enum.map(function_specs, fn spec -> spacer = if arity == 0, do: "", else: ", " spec_string = name |> Code.Typespec.spec_to_quoted(spec) |> Macro.to_string() |> String.replace(") ::", "#{spacer}:call | :cast) ::") "@#{:spec} #{spec_string}" end) else _ -> raise("missing specs for #{name}/#{arity} inside #{to_string(module())}") end end defp module(:simple) do module() |> Module.split() |> List.last() end defp module() do app_name() |> to_string() |> String.capitalize() |> String.to_atom() |> List.wrap() |> Module.safe_concat() end defp app_name(), do: __MODULE__ |> Application.get_application() end