# Copyright(c) 2015-2023 ACCESS CO., LTD. All rights reserved. use Croma defmodule Antikythera.GearApplication.ErrorHandler do @moduledoc """ Helper module for gear's custom error handler. To customize HTTP responses returned on errors, gear must implement an error handler module which - is named `YourGear.Controller.Error`, and - defines the following functions: - Mandatory error handlers - `error(Antikythera.Conn.t, Antikythera.ErrorReason.gear_action_error_reason) :: Antikythera.Conn.t` - `no_route(Antikythera.Conn.t) :: Antikythera.Conn.t` - `bad_request(Antikythera.Conn.t) :: Antikythera.Conn.t` - Optional error handlers - `bad_executor_pool_id(Antikythera.Conn.t, Antikythera.ExecutorPool.BadIdReason.t) :: Antikythera.Conn.t` - `ws_too_many_connections(Antikythera.Conn.t) :: Antikythera.Conn.t` (when your gear uses websocket) This module generates `YourGear.error_handler_module/0` function, which is called by antikythera when handling errors. """ alias Antikythera.GearName alias AntikytheraCore.GearModule @all_handlers [ error: 2, no_route: 1, bad_request: 1 ] @doc false defun find_error_handler_module(gear_name :: v[GearName.t()]) :: nil | module do # during compilation of gear, allowed to call unsafe function mod = GearModule.error_handler_unsafe(gear_name) try do exported_funs = mod.module_info(:exports) not_defined_handlers = Enum.reject(@all_handlers, &(&1 in exported_funs)) if Enum.empty?(not_defined_handlers) do mod else IO.puts(""" [antikythera] warning: #{mod} exists but some of the error handlers are not defined: #{inspect(not_defined_handlers)}; [antikythera] #{mod} is not eligible for a custom error handler module. """) nil end rescue # module not defined UndefinedFunctionError -> nil end end defmacro __using__(_) do quote do # Assuming that module attribute `@gear_name` is defined in the __CALLER__'s context @error_handler_module Antikythera.GearApplication.ErrorHandler.find_error_handler_module( @gear_name ) defun error_handler_module() :: nil | module do @error_handler_module end end end end