Current section

Files

Jump to
ex_cldr_calendars lib cldr calendar compiler.ex
Raw

lib/cldr/calendar/compiler.ex

defmodule Cldr.Calendar.Compiler do
@moduledoc false
use GenServer
require Logger
alias Cldr.Calendar.Config
@default_compiler_timeout 7_000
def start_link(state) do
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
def create_calendar(calendar_module, calendar_type, config) do
# Fast path: if the calendar module is already loaded there is nothing
# to compile. Short-circuit before touching the GenServer so concurrent
# callers don't serialise behind whichever compile happens to be in
# flight. The GenServer's own Code.ensure_loaded?/1 check in
# handle_call/3 still guards against a race when two callers request
# the same not-yet-loaded module at the same time.
if Code.ensure_loaded?(calendar_module) do
{:ok, calendar_module}
else
do_create_calendar(calendar_module, calendar_type, config)
end
end
defp do_create_calendar(calendar_module, calendar_type, config) do
config = Keyword.put(config, :calendar, calendar_module)
structured_config = Config.extract_options(config)
with {:ok, config} <- Config.validate_config(structured_config, calendar_type) do
calendar_type =
calendar_type
|> to_string
|> String.capitalize()
config =
config
|> Map.from_struct()
|> Map.to_list()
contents =
quote do
use unquote(Module.concat(Cldr.Calendar.Base, calendar_type)),
unquote(Macro.escape(config))
end
gen_server_timeout =
Application.get_env(:ex_cldr_calendars, :compiler_timeout, @default_compiler_timeout)
Logger.debug("Requesting compilation of calendar module #{inspect(calendar_module)}")
GenServer.call(
__MODULE__,
{
:compile,
calendar_module,
contents,
Macro.Env.location(__ENV__)
},
gen_server_timeout
)
end
end
## Callbacks
@impl true
def init(state) do
{:ok, state}
end
@impl true
def handle_call({:compile, module, contents, env}, _from, state) do
cond do
Code.ensure_loaded?(module) ->
Logger.debug("Calendar module #{inspect(module)} already defined, returning it")
{:reply, {:ok, module}, state}
{:module, module, _, :ok} = Module.create(module, contents, env) ->
Logger.debug("Created calendar module #{inspect(module)}")
{:reply, {:ok, module}, state}
end
end
end