defmodule AOC.IEx do @moduledoc """ IEx helpers for advent of code. This module contains various helpers that make it easy to call procedures in your solution modules. This is particularly useful when you are testing your solutions from within iex. In order to avoid prefixing all calls with `AOC.IEx`, we recommend adding `import AOC.IEx` to your [`.iex.exs` file](https://hexdocs.pm/iex/IEx.html#module-the-iex-exs-file). ## Requirements and `AOC.aoc/3` In order to find a module for a given day and year, this module expects the module to have the name `Y.D`. This is automatically the case if the `AOC.aoc/3` macro was used to build the solution module. Besides this, it is expected that the solutions for part 1 and part 2 are defined in non-private functions named `p1` and `p2`. ## Using this module The `p1/0` and `p2/0` functions can be used to call the `p1` and `p2` functions of your solution module. By default, these functions are called on the module that represents the current day. The current day (and year) is determined by `NaiveDateTime.local_now/0`. If it is past midnight, or if you wish to solve an older challenge, there are a few options at your disposal: - `p1/2` and `p2/2` accept a day and year argument. - `p1/1` and `p1/1` accept a day argument and uses the current year by default. - The current year and day can be configured through the `:advent_of_code_utils` application environment. For instance, you can set the year to `1991` and the day to `8` by placing the following in your `config/config.exs`: ``` import Config config :advent_of_code_utils, day: 8, year: 1991 ``` To summarise, the day or year is determined according to the following rules: 1. If year or day was passed as an argument (to `p1/2`, `p1/1`, `p2/2` or `p2/1`) it is always used. 2. If `:year` or `:day` is present in the `:advent_of_code_utils` application environment, it is used. 3. The `year` or `day` returned by `NaiveDateTime.local_now/0` is used. ## Automatic recompilation It is often necessary to recompile the current `mix` project before running `p1` or `p2`. To avoid manually doing this, all the functions in this module will recompile the current mix project (with `IEx.Helpers.recompile/1`) before calling `p1` or `p2` when `:auto_compile?` is set to `true` in the `:advent_of_code_utils` application environment. Auto reload can be enabled by adding the following to your `config/config.exs`: ``` import Config config :advent_of_code_utils, auto_compile?: true ``` """ alias AOC.Helpers defp mix_started? do Application.started_applications() |> Enum.find(false, fn {name, _, _} -> name == :mix end) end defp maybe_compile() do compile? = Application.get_env(:advent_of_code_utils, :auto_compile?, false) if(compile? and mix_started?(), do: IEx.Helpers.recompile()) end @doc """ Get the module name for `year`, `day`. This function may cause recompilation if `auto_compile?` is enabled. ## Examples iex> mod(1991, 8) Y1991.D8 """ def mod(year, day) do maybe_compile() Helpers.module_name(year, day) end @doc """ Get the module name for `day`, lookup `year`. `year` is fetched from the application environment, otherwise the local time is used. Please refer to the module documentation for additional information. This function may cause recompilation if `auto_compile?` is enabled. ## Examples iex> Application.put_env(:advent_of_code_utils, :year, 1991) iex> mod(8) Y1991.D8 iex> Application.put_env(:advent_of_code_utils, :year, 2000) iex> mod(3) Y2000.D3 """ def mod(day) do maybe_compile() Helpers.module_name(Helpers.year(), day) end @doc """ Get the module name for the current or configured `day` and `year`. `day` and `year` are fetched from the application environment, the local time is used if they are not available. Please refer to the module documentation for additional information. This function may cause recompilation if `auto_compile?` is enabled. ## Examples iex> Application.put_env(:advent_of_code_utils, :year, 1991) iex> Application.put_env(:advent_of_code_utils, :day, 8) iex> mod() Y1991.D8 iex> Application.put_env(:advent_of_code_utils, :year, 2000) iex> Application.put_env(:advent_of_code_utils, :day, 3) iex> mod() Y2000.D3 """ def mod do maybe_compile() Helpers.module_name(Helpers.year(), Helpers.day()) end @doc """ Call `Y.D.p1()` `day` and `year` are fetched from the application environment, the local time is used if they are not available. Please refer to the module documentation for additional information. This function may cause recompilation if `auto_compile?` is enabled. """ def p1(), do: p1(Helpers.year(), Helpers.day()) @doc """ Call `Y.D.p2()` `day` and `year` are fetched from the application environment, the local time is used if they are not available. Please refer to the module documentation for additional information. This function may cause recompilation if `auto_compile?` is enabled. """ def p2(), do: p2(Helpers.year(), Helpers.day()) @doc """ Call `Y.D.p1()` `year` is fetched from the application environment, the local time is used if it is not available. Please refer to the module documentation for additional information. This function may cause recompilation if `auto_compile?` is enabled. """ def p1(day), do: p1(Helpers.year(), day) @doc """ Call `Y.D.p2()` `year` is fetched from the application environment, the local time is used if it is not available. Please refer to the module documentation for additional information. This function may cause recompilation if `auto_compile?` is enabled. """ def p2(day), do: p2(Helpers.year(), day) @doc """ Call `Y.D.p1()` This function may cause recompilation if `auto_compile?` is enabled. """ def p1(year, day), do: mod(year, day).p1() @doc """ Call `Y.D.p2()` This function may cause recompilation if `auto_compile?` is enabled. """ def p2(year, day), do: mod(year, day).p2() end