diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml index 78a21f5..43512b3 100644 --- a/.github/workflows/auto-pr.yml +++ b/.github/workflows/auto-pr.yml @@ -7,7 +7,7 @@ on: - master permissions: - contents: write + contents: read pull-requests: write jobs: @@ -18,21 +18,14 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Create Pull Request - id: cpr - uses: peter-evans/create-pull-request@v7 + uses: repo-sync/pull-request@v2 with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "Auto-created PR from ${{ github.ref_name }}" - title: "${{ github.ref_name }}" - body: | - This PR was automatically created from branch `${{ github.ref_name }}`. - branch: ${{ github.ref_name }} - base: main - assignees: ${{ github.actor }} - draft: false - delete-branch: false + source_branch: ${{ github.ref_name }} + destination_branch: main + pr_title: ${{ github.ref_name }} + pr_body: "This PR was automatically created from branch `${{ github.ref_name }}`." + pr_assignee: ${{ github.actor }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/then.ex b/lib/then.ex index 732b2cf..1327740 100644 --- a/lib/then.ex +++ b/lib/then.ex @@ -89,10 +89,11 @@ defmodule Then do Module.delete_attribute(env.module, :then) arity = length(args) - existing_functions = Module.get_attribute(env.module, :functions_with_then) || [] - function_key = {function_name, arity} + validated_callback = validate_callback_format(then_callback, env) - if Enum.any?(existing_functions, fn {name, ar, _callback} -> {name, ar} == function_key end) do + existing_functions = Module.get_attribute(env.module, :functions_with_then, []) + + if function_already_has_then?(existing_functions, function_name, arity) do raise CompileError, file: env.file, line: env.line, @@ -100,7 +101,7 @@ defmodule Then do "Only one @then per function is allowed." end - Module.put_attribute(env.module, :functions_with_then, {function_name, arity, then_callback}) + Module.put_attribute(env.module, :functions_with_then, {function_name, arity, validated_callback}) end end @@ -108,27 +109,63 @@ defmodule Then do :ok end - defmacro __before_compile__(env) do - functions_with_then = Module.get_attribute(env.module, :functions_with_then) || [] + defp function_already_has_then?(existing_functions, function_name, arity) do + Enum.any?(existing_functions, fn {name, ar, _callback} -> + {name, ar} == {function_name, arity} + end) + end - wrapped_functions = - functions_with_then - |> Enum.map(fn {function_name, arity, callback_name} -> - args = Macro.generate_arguments(arity, __MODULE__) + defp validate_callback_format(callback, env) do + case callback do + atom when is_atom(atom) -> + {:local, atom} - quote do - defoverridable [{unquote(function_name), unquote(arity)}] + {module, function} when is_atom(module) and is_atom(function) -> + {:external, module, function} - def unquote(function_name)(unquote_splicing(args)) do - result = super(unquote_splicing(args)) - unquote(callback_name)(result) - result - end - end - end) + _ -> + raise CompileError, + file: env.file, + line: env.line, + description: "Invalid @then format. Expected :function_name or {Module, :function_name}, got: #{inspect(callback)}" + end + end + + defmacro __before_compile__(env) do + wrapped_functions = + env.module + |> Module.get_attribute(:functions_with_then, []) + |> Enum.map(&generate_wrapped_function/1) quote do unquote_splicing(wrapped_functions) end end + + defp generate_wrapped_function({function_name, arity, callback_spec}) do + args = Macro.generate_arguments(arity, __MODULE__) + callback_call = generate_callback_call(callback_spec) + + quote do + defoverridable [{unquote(function_name), unquote(arity)}] + + def unquote(function_name)(unquote_splicing(args)) do + result = super(unquote_splicing(args)) + unquote(callback_call) + result + end + end + end + + defp generate_callback_call({:local, function_name}) do + quote do + unquote(function_name)(result) + end + end + + defp generate_callback_call({:external, module, function_name}) do + quote do + unquote(module).unquote(function_name)(result) + end + end end