From 375478d31b5edf77f60ce3396d0853ce047609dc Mon Sep 17 00:00:00 2001
From: Zach Perrault <zach.perrault@gmail.com>
Date: Wed, 3 Nov 2021 10:36:53 -0400
Subject: [PATCH] feat: solution generator

---
 2021/lib/mix/advent_of_code.ex                |  8 ++
 .../mix/tasks/advent_of_code.gen.solution.ex  | 75 ++++++++++++++++++-
 2021/mix.exs                                  |  2 +-
 3 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/2021/lib/mix/advent_of_code.ex b/2021/lib/mix/advent_of_code.ex
index 7c6b7c2..a012629 100644
--- a/2021/lib/mix/advent_of_code.ex
+++ b/2021/lib/mix/advent_of_code.ex
@@ -2,4 +2,12 @@ defmodule Mix.AdventOfCode do
   @moduledoc """
   Helpers for `AdventOfCode` mix tasks.
   """
+
+  def day_module(day) do
+    Module.concat(AdventOfCode, Macro.camelize("Day#{day}"))
+  end
+
+  def part_module(day, part) do
+    Module.concat(day_module(day), Macro.camelize("Part#{part}"))
+  end
 end
diff --git a/2021/lib/mix/tasks/advent_of_code.gen.solution.ex b/2021/lib/mix/tasks/advent_of_code.gen.solution.ex
index 2de107d..f1d1165 100644
--- a/2021/lib/mix/tasks/advent_of_code.gen.solution.ex
+++ b/2021/lib/mix/tasks/advent_of_code.gen.solution.ex
@@ -1,6 +1,7 @@
 defmodule Mix.Tasks.AdventOfCode.Gen.Solution do
   use Mix.Task
   import Mix.Generator
+  import Mix.AdventOfCode
 
   @shortdoc "Generate a new solution module"
 
@@ -15,7 +16,79 @@ defmodule Mix.Tasks.AdventOfCode.Gen.Solution do
       $ mix advent_of_code.gen.solution 2
   """
 
+  @switches []
+
   @impl Mix.Task
-  def run(_args) do
+  def run(args) do
+    case OptionParser.parse!(args, switches: @switches) do
+      {_, [day]} ->
+        day_module = day_module(day)
+        day_contents = day_template(mod: day_module) |> Code.format_string!()
+        day_path = Path.join("lib", Macro.underscore(day_module))
+        day_tests_path = Path.join("test", Macro.underscore(day_module))
+        day_file = "#{day_path}.ex"
+
+        create_directory(day_path)
+        create_directory(day_tests_path)
+        create_file(day_file, day_contents)
+
+        for part <- 1..2 do
+          part_module = part_module(day, part)
+
+          part_contents =
+            part_template(mod: part_module, day_mod: day_module) |> Code.format_string!()
+
+          part_file = Path.join("lib", "#{Macro.underscore(part_module)}.ex")
+          create_file(part_file, part_contents)
+
+          part_test_module = Module.concat(day_module, Macro.camelize("Part#{part}Test"))
+
+          part_test_contents =
+            part_test_template(mod: part_module, test_mod: part_test_module)
+            |> Code.format_string!()
+
+          part_test_file =
+            Path.join(day_tests_path, "#{Macro.underscore("part_#{part}_test")}.exs")
+
+          create_file(part_test_file, part_test_contents)
+        end
+
+      _ ->
+        Mix.raise("Unknown arguments.")
+    end
   end
+
+  embed_template(:day, ~S"""
+    defmodule <%= inspect(@mod) %> do
+
+    end
+  """)
+
+  embed_template(:part, ~S"""
+    defmodule <%= inspect(@mod) %> do
+      alias AdventOfCode.PuzzleSolver
+      use PuzzleSolver
+
+      import <%= inspect(@day_mod) %>, warn: false
+
+      @impl PuzzleSolver
+      def solve(_input_stream) do
+        :ok |> to_string()
+      end
+    end
+  """)
+
+  embed_template(:part_test, ~S[
+    defmodule <%= inspect(@test_mod) %> do
+      use AdventOfCode.PuzzleCase, module: <%= inspect(@mod) %>
+
+      test "returns :ok" do
+        input = ~S"""
+        input
+        """
+
+        assert_solution input, "ok"
+      end
+    end
+  ])
 end
diff --git a/2021/mix.exs b/2021/mix.exs
index 99a37cc..a8ad075 100644
--- a/2021/mix.exs
+++ b/2021/mix.exs
@@ -15,7 +15,7 @@ defmodule AdventOfCode.MixProject do
   # Run "mix help compile.app" to learn about applications.
   def application do
     [
-      extra_applications: [:logger]
+      extra_applications: [:logger, :eex]
     ]
   end