|
3 | 3 |
|
4 | 4 | defmodule Mix.Gleam do |
5 | 5 | # Version that introduced `gleam export package-information` command |
6 | | - @required_gleam_version ">= 1.10.0" |
| 6 | + @gleam_version_requirement ">= 1.10.0" |
7 | 7 |
|
| 8 | + @spec load_config(Path.t()) :: config :: map() |
8 | 9 | def load_config(dir) do |
9 | 10 | File.cd!(dir, fn -> |
10 | | - gleam!(~W(export package-information --out /dev/stdout)) |
11 | | - |> JSON.decode!() |
12 | | - |> Map.fetch!("gleam.toml") |
13 | | - |> parse_config() |
| 11 | + with {:ok, output} <- |
| 12 | + gleam(~W(export package-information --out /dev/stdout)), |
| 13 | + json <- JSON.decode!(output), |
| 14 | + {:ok, gleam_toml} <- Map.fetch(json, "gleam.toml") do |
| 15 | + parse_config(gleam_toml) |
| 16 | + else |
| 17 | + :error -> |
| 18 | + {:error, "\"gleam.toml\" key not found in \"gleam export package-information\" output"} |
| 19 | + |
| 20 | + {:error, message} -> |
| 21 | + {:error, message} |
| 22 | + end |
| 23 | + |> assert_ok_value!() |
14 | 24 | end) |
15 | 25 | end |
16 | 26 |
|
17 | | - def parse_config(json) do |
| 27 | + @spec parse_config(map()) :: {:ok, config :: map()} | {:error, message :: binary()} |
| 28 | + def parse_config(json) when is_map(json) do |
18 | 29 | deps = |
19 | 30 | Map.get(json, "dependencies", %{}) |
20 | | - |> Enum.map(&parse_dep/1) |
| 31 | + |> Enum.map(&parse_dep!/1) |
21 | 32 |
|
22 | 33 | dev_deps = |
23 | 34 | Map.get(json, "dev-dependencies", %{}) |
24 | | - |> Enum.map(&parse_dep(&1, only: [:dev, :test])) |
25 | | - |
26 | | - %{ |
27 | | - name: Map.fetch!(json, "name"), |
28 | | - version: Map.fetch!(json, "version"), |
29 | | - deps: deps ++ dev_deps |
30 | | - } |
31 | | - |> maybe_gleam_version(json) |
32 | | - |> maybe_erlang_opts(json["erlang"]) |
33 | | - rescue |
34 | | - KeyError -> |
35 | | - Mix.raise("Command \"gleam export package-information\" unexpected format: \n" <> json) |
| 35 | + |> Enum.map(&parse_dep!(&1, only: [:dev, :test])) |
| 36 | + |
| 37 | + with {:ok, name} <- Map.fetch(json, "name"), |
| 38 | + {:ok, version} <- Map.fetch(json, "version") do |
| 39 | + config = |
| 40 | + %{ |
| 41 | + name: name, |
| 42 | + version: version, |
| 43 | + deps: deps ++ dev_deps |
| 44 | + } |
| 45 | + |> maybe_gleam_version(json) |
| 46 | + |> maybe_erlang_opts(json["erlang"]) |
| 47 | + |
| 48 | + {:ok, config} |
| 49 | + else |
| 50 | + :error -> |
| 51 | + {:error, |
| 52 | + "Command \"gleam export package-information\" unexpected format: \n" <> |
| 53 | + inspect(json, pretty: true, limit: :infinity)} |
| 54 | + end |
36 | 55 | end |
37 | 56 |
|
38 | | - defp parse_dep({dep, requirement}, opts \\ []) do |
39 | | - dep = String.to_atom(dep) |
| 57 | + defp parse_dep!({dep, requirement}, opts \\ []) do |
| 58 | + String.to_atom(dep) |
| 59 | + |> build_dep_spec(requirement, opts) |
| 60 | + |> assert_ok_value!() |
| 61 | + end |
40 | 62 |
|
41 | | - spec = |
42 | | - case requirement do |
43 | | - %{"version" => version} -> |
44 | | - {dep, version, opts} |
| 63 | + defp build_dep_spec(dep, %{"version" => version}, []), |
| 64 | + do: {:ok, {dep, version}} |
45 | 65 |
|
46 | | - %{"path" => path} -> |
47 | | - {dep, Keyword.merge(opts, path: Path.expand(path))} |
| 66 | + defp build_dep_spec(dep, %{"version" => version}, opts), |
| 67 | + do: {:ok, {dep, version, opts}} |
48 | 68 |
|
49 | | - %{"git" => git, "ref" => ref} -> |
50 | | - {dep, git: git, ref: ref} |
| 69 | + defp build_dep_spec(dep, %{"path" => path}, opts), |
| 70 | + do: {:ok, {dep, Keyword.merge(opts, path: Path.expand(path))}} |
51 | 71 |
|
52 | | - _ -> |
53 | | - Mix.raise("Gleam package #{dep} has unsupported requirement: #{inspect(requirement)}") |
54 | | - end |
| 72 | + defp build_dep_spec(dep, %{"git" => git, "ref" => ref}, _opts), |
| 73 | + do: {:ok, {dep, git: git, ref: ref}} |
55 | 74 |
|
56 | | - case spec do |
57 | | - {dep, version, []} -> {dep, version} |
58 | | - spec -> spec |
59 | | - end |
60 | | - end |
| 75 | + defp build_dep_spec(dep, requirement, _opts), |
| 76 | + do: {:error, "Gleam package #{dep} has unsupported requirement: #{inspect(requirement)}"} |
61 | 77 |
|
62 | 78 | defp maybe_gleam_version(config, json) do |
63 | 79 | case json["gleam"] do |
@@ -86,37 +102,58 @@ defmodule Mix.Gleam do |
86 | 102 | Map.put(config, :application, application) |
87 | 103 | end |
88 | 104 |
|
89 | | - def require!() do |
90 | | - available_version() |
91 | | - |> Version.match?(@required_gleam_version) |
| 105 | + @spec requirements!() :: :ok |
| 106 | + def requirements!() do |
| 107 | + case fetch_gleam_version() do |
| 108 | + {:ok, gleam_version} -> |
| 109 | + if Version.match?(gleam_version, @gleam_version_requirement) do |
| 110 | + {:ok, :ok} |
| 111 | + else |
| 112 | + {:error, |
| 113 | + "Current Gleam version does not meet minimum requirements " <> |
| 114 | + "#{@gleam_version_requirement}), got: #{gleam_version}"} |
| 115 | + end |
| 116 | + |
| 117 | + {:error, message} -> |
| 118 | + {:error, message} |
| 119 | + end |
| 120 | + |> assert_ok_value!() |
92 | 121 | end |
93 | 122 |
|
94 | | - defp available_version do |
95 | | - case gleam!(["--version"]) do |
96 | | - "gleam " <> version -> Version.parse!(version) |> Version.to_string() |
97 | | - output -> Mix.raise("Command \"gleam --version\" unexpected format: #{output}") |
| 123 | + defp fetch_gleam_version() do |
| 124 | + case gleam(["--version"]) do |
| 125 | + {:ok, version} -> |
| 126 | + case Version.parse(version) do |
| 127 | + {:ok, parsed_version} -> |
| 128 | + {:ok, Version.to_string(parsed_version)} |
| 129 | + |
| 130 | + :error -> |
| 131 | + {:error, "Command \"gleam --version\" invalid version format: #{version}"} |
| 132 | + end |
| 133 | + |
| 134 | + {:error, output} -> |
| 135 | + {:error, "Command \"gleam --version\" unexpected format: #{output}"} |
98 | 136 | end |
99 | | - rescue |
100 | | - e in Version.InvalidVersionError -> |
101 | | - Mix.raise("Command \"gleam --version\" invalid version format: #{e.version}") |
102 | 137 | end |
103 | 138 |
|
104 | | - defp gleam!(args) do |
| 139 | + defp gleam(args) do |
105 | 140 | System.cmd("gleam", args) |
106 | 141 | catch |
107 | 142 | :error, :enoent -> |
108 | | - Mix.raise( |
109 | | - "The \"gleam\" executable is not available in your PATH. " <> |
110 | | - "Please install it, as one of your dependencies requires it" |
111 | | - ) |
| 143 | + {:error, |
| 144 | + "The \"gleam\" executable is not available in your PATH. " <> |
| 145 | + "Please install it, as one of your dependencies requires it"} |
112 | 146 | else |
113 | 147 | {response, 0} -> |
114 | | - String.trim(response) |
| 148 | + {:ok, String.trim(response)} |
115 | 149 |
|
116 | 150 | {response, _} when is_binary(response) -> |
117 | | - Mix.raise("Command \"gleam #{Enum.join(args, " ")}\" failed with reason: #{response}") |
| 151 | + {:error, "Command \"gleam #{Enum.join(args, " ")}\" failed with reason: #{response}"} |
118 | 152 |
|
119 | 153 | {_, _} -> |
120 | | - Mix.raise("Command \"gleam #{Enum.join(args, " ")}\" failed") |
| 154 | + {:error, "Command \"gleam #{Enum.join(args, " ")}\" failed"} |
121 | 155 | end |
| 156 | + |
| 157 | + defp assert_ok_value!({:ok, term}), do: term |
| 158 | + defp assert_ok_value!({:error, message}) when is_binary(message), do: Mix.raise(message) |
122 | 159 | end |
0 commit comments