Packages
igniter
0.3.60
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/igniter/util/info.ex
defmodule Igniter.Util.Info do
@moduledoc false
require Logger
alias Igniter.Mix.Task.Info
def compose_install_and_validate!(
igniter \\ Igniter.new(),
argv,
schema,
task_name,
opts,
acc \\ []
) do
schema = %{
schema
| flag_conflicts: flag_groups(schema, task_name),
alias_conflicts: alias_groups(schema, task_name)
}
schema = recursively_compose_schema(schema, argv, task_name, opts)
case schema.installs do
[] ->
igniter =
igniter
|> add_deps(
List.wrap(schema.adds_deps),
opts
)
|> Igniter.apply_and_fetch_dependencies(opts)
raise_on_conflicts!(schema, argv)
{igniter, Enum.uniq(acc), validate!(argv, schema, task_name)}
installs ->
schema = %{schema | installs: []}
install_names = Keyword.keys(installs)
igniter
|> add_deps(
List.wrap(installs),
opts
)
|> Igniter.apply_and_fetch_dependencies(opts)
|> maybe_set_only(install_names, argv, task_name)
|> compose_install_and_validate!(
argv,
%{
schema
| composes: Enum.map(install_names, &"#{&1}.install"),
installs: [],
adds_deps: schema.adds_deps
},
task_name,
Keyword.delete(opts, :only),
acc ++ install_names
)
end
end
defp raise_on_conflicts!(schema, argv) do
flag_conflicts =
schema.flag_conflicts
|> Map.drop(Keyword.keys(Igniter.Mix.Task.Info.global_options()[:switches]))
|> Enum.filter(fn {_, list} ->
Enum.count(list) > 1
end)
|> Map.new(fn {k, v} ->
{"--" <> String.replace(to_string(k), "_", "-"), v}
end)
alias_conflicts =
schema.alias_conflicts
|> Map.drop(Keyword.keys(Igniter.Mix.Task.Info.global_options()[:aliases]))
|> Enum.flat_map(fn {k, list} ->
case Enum.uniq_by(list, &elem(&1, 0)) do
[] -> []
[_] -> []
list -> [{k, Enum.uniq(Enum.map(list, &elem(&1, 1)))}]
end
end)
|> Map.new(fn {k, v} ->
{"-" <> String.replace(to_string(k), "_", "-"), v}
end)
if Enum.empty?(flag_conflicts) and Enum.empty?(alias_conflicts) do
:ok
else
Enum.each(argv, fn arg ->
cond do
conflicting = flag_conflicts[arg] ->
Mix.shell().error("""
Ambiguous flag provided: `#{arg}`
The tasks or task groups `#{Enum.join(conflicting, ", ")}` all define the flag `#{arg}`.
To disambiguate, provide the arg as `--<prefix>.#{String.trim_leading(arg, "--")}`,
where `<prefix>` is the task or task group name.
For example:
`--#{Enum.at(conflicting, 0)}.#{String.trim_leading(arg, "--")}`
""")
exit({:shutdown, 2})
conflicting = alias_conflicts[arg] ->
Mix.shell().error("""
Ambiguous flag provided: `#{arg}`
The tasks or task groups `#{Enum.join(conflicting, ", ")}` all define the flag `#{arg}`.
To disambiguate, provide the arg as `-<prefix>.#{String.trim_leading(arg, "-")}`,
where `<prefix>` is the task or task group name.
For example:
`--#{Enum.at(conflicting, 0)}.#{String.trim_leading(arg, "-")}`
""")
exit({:shutdown, 2})
true ->
:ok
end
end)
end
end
defp maybe_set_only(igniter, install_names, argv, parent) do
Enum.reduce(install_names, igniter, fn install, igniter ->
composing_task = "#{install}.install"
with composing_task when not is_nil(composing_task) <- Mix.Task.get(composing_task),
true <- function_exported?(composing_task, :info, 2),
composing_schema when not is_nil(composing_schema) <-
composing_task.info(argv, parent),
only when not is_nil(only) <- composing_schema.only do
Igniter.Project.Deps.set_dep_option(igniter, install, :only, only)
else
_ ->
igniter
end
end)
end
defp add_deps(igniter, add_deps, opts) do
Enum.reduce(add_deps, igniter, fn dependency, igniter ->
with {_, _, dep_opts} <- dependency,
only when not is_nil(only) <- dep_opts[:only],
false <- Mix.env() in only do
Igniter.add_warning(igniter, """
Dependency #{inspect(dependency)} could not be installed,
because it is configured to be installed with `only: #{inspect(only)}`.
Please install it manually, for example:
`MIX_ENV=#{Enum.at(only, 0)} mix igniter.install #{dependency}`.
""")
else
_ ->
Igniter.Project.Deps.add_dep(
igniter,
dependency,
Keyword.merge(opts, notify_on_present?: true, yes?: !!opts[:yes])
)
end
end)
end
def validate!(argv, schema, task_name)
def validate!(_argv, nil, _task_name), do: {[], []}
def validate!(argv, schema, task_name) do
group = group(schema, task_name)
argv = args_for_group(argv, group)
merged_schema = recursively_compose_schema(schema, argv, task_name, [])
options_key =
if merged_schema.extra_args? do
:switches
else
:strict
end
OptionParser.parse!(
argv,
[
{options_key, merged_schema.schema || []},
{:aliases, merged_schema.aliases || []}
]
)
end
@doc false
def args_for_group(argv, group) do
case argv do
["--" <> arg, "-" <> v2 | rest] ->
with true <- String.starts_with?(arg, "#{group}."),
[_, actual_arg] <- String.split(arg, "#{group}.", parts: 2) do
["--" <> actual_arg] ++ args_for_group(["-" <> v2 | rest], group)
else
_ ->
rest_args = args_for_group(["-" <> v2 | rest], group)
if String.contains?(arg, ".") do
rest_args
else
["--#{arg}"] ++ rest_args
end
end
["-" <> arg, "-" <> v2 | rest] ->
with true <- String.starts_with?(arg, "#{group}."),
[_, actual_arg] <- String.split(arg, "#{group}.", parts: 2) do
["-" <> actual_arg] ++ args_for_group(["-" <> v2 | rest], group)
else
_ ->
rest_args = args_for_group(["-" <> v2 | rest], group)
if String.contains?(arg, ".") do
rest_args
else
["-#{arg}"] ++ rest_args
end
end
["--" <> arg | rest] ->
with true <- String.starts_with?(arg, "#{group}."),
[_, actual_arg] <- String.split(arg, "#{group}.", parts: 2) do
["--" <> actual_arg] ++ args_for_group(rest, group)
else
_ ->
rest_args = args_for_group(rest, group)
if String.contains?(arg, ".") do
rest_args
else
["--#{arg}"] ++ rest_args
end
end
["-" <> arg | rest] ->
with true <- String.starts_with?(arg, "#{group}."),
[_, actual_arg] <- String.split(arg, "#{group}.", parts: 2) do
["-" <> actual_arg] ++ args_for_group(rest, group)
else
_ ->
rest_args = args_for_group(rest, group)
if String.contains?(arg, ".") do
rest_args
else
["-#{arg}"] ++ rest_args
end
end
[v1 | rest] ->
[v1] ++ args_for_group(rest, group)
[] ->
[]
end
end
defp recursively_compose_schema(%Info{composes: []} = schema, _argv, _parent, _opts), do: schema
defp recursively_compose_schema(%Info{composes: [compose | rest]} = schema, argv, parent, opts) do
with composing_task when not is_nil(composing_task) <- Mix.Task.get(compose),
true <- function_exported?(composing_task, :info, 2),
composing_schema when not is_nil(composing_schema) <- composing_task.info(argv, parent) do
composing_task_name = Mix.Task.task_name(composing_task)
composing_schema =
if opts[:only] do
%{composing_schema | only: opts[:only]}
else
composing_schema
end
recursively_compose_schema(
%Info{
schema
| schema:
merge_schemas(
schema.schema,
composing_schema.schema
),
aliases:
merge_aliases(
schema.aliases,
composing_schema.aliases
),
flag_conflicts: flag_conflicts(schema, composing_schema, composing_task_name),
alias_conflicts: alias_conflicts(schema, composing_schema, composing_task_name),
composes: rest,
extra_args?: schema.extra_args? || composing_schema.extra_args?,
installs: Keyword.merge(composing_schema.installs, schema.installs),
adds_deps: Keyword.merge(composing_schema.adds_deps, schema.adds_deps)
},
argv,
parent,
Keyword.delete(opts, :only)
)
|> Map.put(:composes, List.wrap(composing_schema.composes))
|> recursively_compose_schema(argv, composing_task_name, Keyword.delete(opts, :only))
else
_ ->
recursively_compose_schema(
%{schema | composes: rest},
argv,
parent,
Keyword.delete(opts, :only)
)
end
end
defp flag_conflicts(schema, composing_schema, composing_task_name) do
Map.merge(
schema.flag_conflicts,
flag_groups(composing_schema, composing_task_name),
fn _key, schema_value, composing_value ->
Enum.uniq(schema_value ++ composing_value)
end
)
end
defp alias_conflicts(schema, composing_schema, composing_task_name) do
Map.merge(
schema.alias_conflicts,
alias_groups(composing_schema, composing_task_name),
fn _key, schema_value, composing_value ->
Enum.uniq(schema_value ++ composing_value)
end
)
end
@doc false
def group(%{group: group}, _task_name) when not is_nil(group), do: to_string(group)
def group(_, task_name), do: String.replace(task_name, "_", "-")
defp merge_schemas(schema, composing_schema) do
schema = schema || []
composing_schema = composing_schema || []
Keyword.merge(composing_schema, schema)
end
defp merge_aliases(aliases, composing_aliases) do
aliases = aliases || []
composing_aliases = composing_aliases || []
Keyword.merge(composing_aliases, aliases)
end
defp flag_groups(schema, task_name) do
Map.new(schema.schema, fn {k, _} ->
{k, [group(schema, task_name)]}
end)
end
defp alias_groups(schema, task_name) do
Map.new(schema.aliases, fn {k, v} ->
{k, [{v, group(schema, task_name)}]}
end)
end
end