Skip to content
Draft
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
1 change: 1 addition & 0 deletions blend/req_0_4.mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"},
"sourceror": {:hex, :sourceror, "1.7.1", "599d78f4cc2be7d55c9c4fd0a8d772fd0478e3a50e726697c20d13d02aa056d4", [:mix], [], "hexpm", "cd6f268fe29fa00afbc535e215158680a0662b357dc784646d7dff28ac65a0fc"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
}
107 changes: 107 additions & 0 deletions lib/mix/tasks/curl_req/expand.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
defmodule Mix.Tasks.CurlReq.Expand do
use Mix.Task

def run(_args) do
files =
"lib/**/*.{ex,exs}"
|> Path.wildcard(match_dot: true)
|> Enum.filter(&File.regular?/1)

yes =
"""
Should we expand the ~CURL() in the following files?
#{Enum.join(files, "\n")}

"""
|> Mix.shell().yes?()

if yes do
for file <- files do
expand_file(file)
end
else
Mix.shell().info("Operation aborted")
end
end

defp expand(source) when is_binary(source) do
{_quoted, patches} =
source
|> Sourceror.parse_string!()
|> Macro.postwalk([], fn
{:sigil_CURL, _dot_meta, [{:<<>>, _, _} | _] = _rest} = quoted, patches ->
{:ok, env} = __ENV__ |> Macro.Env.define_import([line: 2], CurlReq)
# TODO: get range when Sourceror.get_range/1 returns nil
if range = Sourceror.get_range(quoted) do
expanded = Macro.expand_once(quoted, env)
reconstructed = reconstruct(expanded)
replacement = reconstructed |> Sourceror.to_string()
patch = %{range: range, change: replacement}
{quoted, [patch | patches]}
else
{quoted, patches}
end

quoted, patches ->
{quoted, patches}
end)

Sourceror.patch_string(source, patches)
end

defp expand_file(filename) do
patched_code =
filename
|> File.read!()
|> expand()

File.write!(filename, patched_code)
end

# Walks and transforms the AST so that
# %{__struct__: Some.Module, key: val, ...}
# becomes
# %Some.Module{key: val, ...}.

defp reconstruct(ast) do
Macro.postwalk(ast, fn
# Match on a literal map: {:%{}, meta, fields}
{:%{}, meta, fields} = node ->
case Keyword.pop(fields, :__struct__) do
{nil, _} ->
# It's just a normal map, not a struct
node

{mod, rest} ->
# We found __struct__: mod
# Turn that into:
#
# {:%, meta, [
# alias_ast_for(mod),
# {:%{}, meta, rest}
# ]}
#
{:%, meta,
[
module_to_alias_ast(mod),
{:%{}, meta, rest}
]}
end

other ->
other
end)
end

# Converts an atom module (e.g. Req.Request)
# into an AST alias: {:__aliases__, [alias: false], [:Req, :Request]}
defp module_to_alias_ast(mod) when is_atom(mod) do
mod
# ["Foo", "Bar", "Baz"]
|> Module.split()
|> Enum.map(&String.to_atom/1)
|> then(fn aliases ->
{:__aliases__, [alias: false], aliases}
end)
end
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ defmodule CurlReq.MixProject do
[
{:req, "~> 0.4.0 or ~> 0.5.0"},
{:jason, "~> 1.4"},
{:sourceror, "~> 1.7"},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
{:blend, "~> 0.4.1", only: :dev}
]
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"},
"sourceror": {:hex, :sourceror, "1.7.1", "599d78f4cc2be7d55c9c4fd0a8d772fd0478e3a50e726697c20d13d02aa056d4", [:mix], [], "hexpm", "cd6f268fe29fa00afbc535e215158680a0662b357dc784646d7dff28ac65a0fc"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
}