mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 21:43:02 +12:00
123 lines
3.3 KiB
Elixir
123 lines
3.3 KiB
Elixir
defmodule Mix.Tasks.Ash.Formatter do
|
|
@moduledoc "Generates a .formatter.exs from a list of extensions, and writes it."
|
|
use Mix.Task
|
|
|
|
@formatter_exs_template """
|
|
# THIS FILE IS AUTOGENERATED USING `mix ash.formatter`
|
|
# DONT MODIFY IT BY HAND
|
|
locals_without_parens = __replace_me__
|
|
|
|
[
|
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
|
locals_without_parens: locals_without_parens,
|
|
export: [
|
|
locals_without_parens: locals_without_parens
|
|
]
|
|
]
|
|
|
|
"""
|
|
|
|
@shortdoc @moduledoc
|
|
@spec run(term) :: no_return
|
|
def run(opts) do
|
|
Mix.Task.run("compile")
|
|
{opts, []} = OptionParser.parse!(opts, strict: [check: :boolean, extensions: :string])
|
|
|
|
unless opts[:extensions] do
|
|
raise "Must supply a comma separated list of extensions to generate a .formatter.exs for"
|
|
end
|
|
|
|
locals_without_parens =
|
|
opts[:extensions]
|
|
|> String.split(",")
|
|
|> Enum.flat_map(fn extension ->
|
|
extension_mod = Module.concat([extension])
|
|
|
|
case Code.ensure_compiled(extension_mod) do
|
|
{:module, _module} -> :ok
|
|
other -> raise "Error ensuring extension compiled #{inspect(other)}"
|
|
end
|
|
|
|
all_entity_builders(extension_mod.sections())
|
|
end)
|
|
|> Enum.uniq()
|
|
|> Enum.sort()
|
|
|
|
contents =
|
|
@formatter_exs_template
|
|
|> String.replace("__replace_me__", inspect(locals_without_parens, limit: :infinity))
|
|
|> Code.format_string!()
|
|
|
|
contents_with_newline = [contents, "\n"]
|
|
|
|
if opts[:check] do
|
|
if File.read!(".formatter.exs") != IO.iodata_to_binary(contents_with_newline) do
|
|
raise """
|
|
.formatter.exs is not up to date!
|
|
|
|
Run the following command and commit the result:
|
|
|
|
mix ash.formatter --extensions #{opts[:extensions]}
|
|
"""
|
|
else
|
|
IO.puts("The current .formatter.exs is correct")
|
|
end
|
|
else
|
|
File.write!(".formatter.exs", contents_with_newline)
|
|
end
|
|
end
|
|
|
|
defp all_entity_builders(sections) do
|
|
Enum.flat_map(sections, fn section ->
|
|
Enum.concat([
|
|
all_entity_option_builders(section),
|
|
section_option_builders(section),
|
|
section_entity_builders(section)
|
|
])
|
|
end)
|
|
end
|
|
|
|
defp section_entity_builders(section) do
|
|
Enum.flat_map(section.entities(), fn entity ->
|
|
entity_builders(entity)
|
|
end) ++ all_entity_builders(section.sections())
|
|
end
|
|
|
|
defp entity_builders(entity) do
|
|
arg_count = Enum.count(entity.args)
|
|
|
|
[{entity.name, arg_count}, {entity.name, arg_count + 1}] ++
|
|
flat_map_nested_entities(entity, &entity_builders/1)
|
|
end
|
|
|
|
defp all_entity_option_builders(section) do
|
|
Enum.flat_map(section.entities, fn entity ->
|
|
entity_option_builders(entity)
|
|
end)
|
|
end
|
|
|
|
defp entity_option_builders(entity) do
|
|
entity.schema
|
|
|> Keyword.drop(entity.args)
|
|
|> Enum.map(fn {key, _schema} ->
|
|
{key, 1}
|
|
end)
|
|
|> Kernel.++(flat_map_nested_entities(entity, &entity_option_builders/1))
|
|
end
|
|
|
|
defp section_option_builders(section) do
|
|
Enum.map(section.schema, fn {key, _} ->
|
|
{key, 1}
|
|
end)
|
|
end
|
|
|
|
defp flat_map_nested_entities(entity, mapper) do
|
|
Enum.flat_map(entity.entities, fn {_, nested_entities} ->
|
|
nested_entities
|
|
|> List.wrap()
|
|
|> Enum.flat_map(fn nested_entity ->
|
|
mapper.(nested_entity)
|
|
end)
|
|
end)
|
|
end
|
|
end
|