Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v0.6.3 - 2024-01-28

- Added `mix gleam.toml [--replace]` task. It generates gleam.toml in Mix project. This might be useful for Gleam tooling support in Mix projects, such as Gleam LSP.

## v0.6.2 - 2023-11-16

- Updated for Elixir v1.15 and Gleam v0.32 compatibility. Make sure to set
Expand Down
14 changes: 11 additions & 3 deletions lib/mix/tasks/gleam/deps/get.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ defmodule Mix.Tasks.Gleam.Deps.Get do

Include this task in your project's `mix.exs` with, e.g.:

def project do
[
def project do [
aliases: ["deps.get": ["deps.get", "gleam.deps.get"]],
]
end
Expand Down Expand Up @@ -49,7 +48,7 @@ defmodule Mix.Tasks.Gleam.Deps.Get do
|> Map.to_list()
|> Enum.map(&Tuple.append(&1, only: [:dev, :test], runtime: false))

deps = deps ++ dev_deps
deps = unmap(deps ++ dev_deps)
# TODO use eex template
File.write!("mix.exs", MixGleam.Config.render_mix(app, version, deps))
:cont
Expand All @@ -70,4 +69,13 @@ defmodule Mix.Tasks.Gleam.Deps.Get do

MixGleam.IO.debug_info("Deps.Get End")
end

defp unmap(x) do
cond do
is_map(x) -> x |> Map.to_list() |> unmap
is_list(x) -> x |> Enum.map(&unmap/1)
is_tuple(x) -> x |> Tuple.to_list() |> unmap |> List.to_tuple()
true -> x
end
end
end
93 changes: 93 additions & 0 deletions lib/mix/tasks/gleam/toml.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
defmodule Mix.Tasks.Gleam.Toml do
use Mix.Task
@shortdoc "Generates gleam.toml in Mix project."
@moduledoc """
#{@shortdoc}

This might be useful for Gleam tooling support in Mix projects, such as Gleam LSP.

Print gleam.toml into stdout:

mix gleam.toml

Replace gleam.toml file:

mix gleam.toml --replace

Automate gleam.toml sync using mix.exs project aliases:

aliases: [
"deps.get": ["deps.get", "gleam.deps.get", "gleam.toml --replace"]
]
"""
@impl true
@shell Mix.shell()
def run(argv) do
replace =
argv
|> OptionParser.parse!(switches: [replace: :boolean])
|> elem(0)
|> Keyword.get(:replace, false)

Mix.Project.get!()
cfg = Mix.Project.config()
cwd = Mix.Project.project_file() |> Path.dirname()

deps =
Mix.Dep.load_and_cache()
|> Enum.flat_map(fn %Mix.Dep{app: dep, opts: opts} ->
dst = Keyword.fetch!(opts, :dest)

if dst |> Path.join("gleam.toml") |> File.regular?() do
[{dep, path: Path.relative_to(dst, cwd)}]
else
[]
end
end)
|> Enum.sort()

toml =
[
name: Keyword.fetch!(cfg, :app),
version: Keyword.fetch!(cfg, :version),
dependencies: deps
]
|> mk_toml

if replace do
cwd
|> Path.join("gleam.toml")
|> File.write!(toml)
else
@shell.info(toml)
end
end

defp mk_toml(x) do
x
|> Enum.map(&mk_toml_row/1)
|> Enum.join("\n")
end

defp mk_toml_row({k, v}), do: "#{mk_toml_key(k)} = #{mk_toml_val(v)}"

defp mk_toml_key(x) when is_atom(x) or is_binary(x) or is_number(x) do
x |> to_string |> inspect
end

defp mk_toml_val(x) do
cond do
is_map(x) or Keyword.keyword?(x) ->
"{#{x |> Enum.map(&mk_toml_row/1) |> Enum.join(", ")}}"

is_list(x) ->
"[#{x |> Enum.map(&mk_toml_val/1) |> Enum.join(", ")}]"

is_number(x) ->
to_string(x)

true ->
mk_toml_key(x)
end
end
end
30 changes: 25 additions & 5 deletions lib/mix_gleam/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ end)

File.read!(path)
|> String.split(definition, trim: true)
|> Stream.map(&tokenize/1)
|> Stream.reject(&({[], nil} == &1))
|> Stream.flat_map(&tokenize/1)
|> Stream.map(&atomize/1)
|> Stream.map(&parse/1)
|> structure
Expand Down Expand Up @@ -167,14 +166,35 @@ end)
cond do
match = Regex.run(table, line) ->
[_, key] = match
{[key], nil}
[{[key], nil}]

match = Regex.run(assignment, line) ->
[_, key, value] = match
{[key], value}

tokenize_inline_table([key], value)

true ->
{[], nil}
[]
end
end

defp tokenize_inline_table(prev_keys, prev_value) do
re = ~r/\{(.+?)\}/

case Regex.run(re, prev_value, capture: :all_but_first) do
[pairs] ->
pairs
|> String.split(",")
|> Stream.map(&String.split(&1, "="))
|> Enum.flat_map(fn [next_key, next_value] ->
tokenize_inline_table(
prev_keys ++ [String.trim(next_key)],
String.trim(next_value)
)
end)

_ ->
[{prev_keys, prev_value}]
end
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule MixGleam.MixProject do
def project do
[
app: :mix_gleam,
version: "0.6.2",
version: "0.6.3",
elixir: "~> 1.9",
start_permanent: Mix.env() == :prod,
name: "mix_gleam",
Expand Down
4 changes: 2 additions & 2 deletions test_projects/basic_project/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ defmodule BasicProject.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:mix_gleam, path: "../../"}
{:gleam_stdlib, "~> 0.32"},
{:gleeunit, "~> 1.0", only: [:dev, :test], runtime: false}
{:gleeunit, "~> 1.0", only: [:dev, :test], runtime: false},
{:mix_gleam, only: [:dev, :test], runtime: false, path: "../../"}
]
end
end
13 changes: 13 additions & 0 deletions test_projects/basic_project/test/basic_project_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule BasicProjectTest do
use ExUnit.Case
import ExUnit.CaptureIO
doctest BasicProject

test "can call Elixir code" do
Expand All @@ -13,4 +14,16 @@ defmodule BasicProjectTest do
test "can call Gleam library" do
assert :[email protected]([1, 2, 3]) == [3, 2, 1]
end

test "gleam.toml mix task" do
lhs = """
"name" = "basic_project"
"version" = "0.1.0"
"dependencies" = {"gleam_stdlib" = {"path" = "deps/gleam_stdlib"}, "gleeunit" = {"path" = "deps/gleeunit"}}
"""

rhs = capture_io(fn -> Mix.Tasks.Gleam.Toml.run([]) end)

assert lhs == rhs
end
end