defmodule Mix.Bonny do
@moduledoc """
Mix task helpers
"""
@doc "Parse CLI input"
@spec parse_args([binary()], Keyword.t(), Keyword.t()) ::
{Keyword.t(), [binary()], [{binary(), nil | binary()}]}
def parse_args(args, defaults, cli_opts \\ []) do
{opts, parsed, invalid} = OptionParser.parse(args, cli_opts)
merged_opts = Keyword.merge(defaults, opts)
{merged_opts, parsed, invalid}
end
@doc """
Capitalizes the string if it does not begin with a capital letter.
"""
def ensure_module_name(string) do
if string =~ ~r/[A-Z].+/, do: string, else: String.capitalize(string)
end
@doc """
Render text to a file.
Special handling for the path "-" will render to STDOUT
"""
@spec render(binary, binary) :: term()
def render(source, "-"), do: IO.puts(source)
def render(source, target) do
Mix.Generator.create_file(target, source)
end
@spec render_template(binary(), binary(), keyword()) :: term()
def render_template(source, "-", bindings) do
EEx.eval_file(source, bindings, trim: true) |> IO.puts()
end
def render_template(source, target, bindings),
do: Mix.Generator.copy_template(source, target, bindings)
@spec copy(binary, binary) :: term()
def copy(source, "-"), do: IO.puts(File.read!(source))
def copy(source, target), do: Mix.Generator.copy_file(source, target)
@doc """
Appends `append_content` to `target`. If `target` does not exist, a new
file with `new_file_content` is created.
"""
@spec append_or_create_with(binary(), binary(), binary(), binary()) :: term()
def append_or_create_with(target, content_to_append, new_file_content, check) do
add_or_create_with(:append, target, content_to_append, new_file_content, check)
end
@doc """
Prepends `append_content` to `target`. If `target` does not exist, a new
file with `new_file_content` is created.
"""
@spec prepend_or_create_with(binary(), binary(), binary(), binary()) :: term()
def prepend_or_create_with(target, content_to_prepend, new_file_content, check) do
add_or_create_with(:prepend, target, content_to_prepend, new_file_content, check)
end
@spec add_or_create_with(:append | :prepend, binary(), binary(), binary(), binary()) :: term()
def add_or_create_with(mode, target, content_to_add, new_file_content, check) do
cond do
!File.exists?(target) ->
Mix.Generator.create_file(target, new_file_content)
:ok
!(File.read!(target) =~ check) ->
add_content(mode, target, content_to_add)
true ->
:ok
end
end
defp add_content(:append, target, content_to_add) do
Owl.IO.puts([
Owl.Data.tag("* appending", :green),
"#{inspect(content_to_add)} to #{target}"
])
{:ok, file} = File.open(target, [:append])
IO.binwrite(file, content_to_add)
File.close(file)
:ok
end
defp add_content(:prepend, target, content_to_add) do
Owl.IO.puts([
Owl.Data.tag("* prepending", :green),
"#{inspect(content_to_add)} to #{target}"
])
file_content = File.read!(target)
File.write!(target, content_to_add <> file_content)
:ok
end
@doc "Get the OTP app name"
@spec app_name() :: binary
def app_name() do
otp_app()
|> Atom.to_string()
|> Macro.camelize()
end
@doc "Get the OTP app name with dashes"
@spec hyphenated_app_name() :: binary
def hyphenated_app_name() do
otp_app()
|> Atom.to_string()
|> String.replace("_", "-")
end
@spec app_dir_name() :: binary
def app_dir_name() do
Macro.underscore(app_name())
end
@spec template(binary) :: binary
def template(name) do
template_dir = Application.app_dir(:bonny, ["priv", "templates", "bonny.gen"])
Path.join(template_dir, name)
end
@spec no_umbrella! :: any
def no_umbrella!() do
if Mix.Project.umbrella?() do
Mix.raise("mix bonny.init and bonny.gen.* can only be run inside an application directory")
end
end
@spec otp_app :: atom
defp otp_app() do
Mix.Project.config() |> Keyword.fetch!(:app)
end
def error(message) do
message |> Owl.Data.tag(:red) |> Owl.IO.puts()
end
end