Skip to content

Commit

Permalink
feat: add `watch command
Browse files Browse the repository at this point in the history
  • Loading branch information
zoedsoupe committed Jan 14, 2024
1 parent 67c3649 commit f7fba2e
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 2 deletions.
33 changes: 32 additions & 1 deletion lib/exlings/cli.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ defmodule Exlings.CLI do
Nexus.help()

defcommand :list, type: :null, doc: "Print all available exercises"

defcommand :verify, type: :null, doc: "Verifies all available exefcises"
defcommand :watch, type: :null, doc: "Waits for a change int oany exercise"

@hint_help "Shows a hint for the specified exercise"
defcommand :hint, type: :string, required: true, doc: @hint_help
Expand Down Expand Up @@ -48,6 +48,11 @@ defmodule Exlings.CLI do
UI.write(:green, "You passed all the exercises")
end

def handle_input(:watch) do
{:ok, _} = Exlings.FileWatcher.start_link(callback: &run_watched_exercise/1)
wait()
end

@impl true
def handle_input(:hint, %{value: exercise}) do
if e = Exercises.find_by(name: exercise) do
Expand All @@ -73,6 +78,32 @@ defmodule Exlings.CLI do
end
end

@spec run_watched_exercise(Path.t()) :: :ok
defp run_watched_exercise(path) do
if e = Exercises.find_by(path: path) do
handle_exercise_result(e)
else
UI.write("Exercise with path #{path} not found!")
end
end

defp wait do
i = IO.read(:line)

cond do
String.match?(i, ~r"quit") ->
UI.write(:green, "Bye by exlings o/")
System.halt(0)

String.match?(i, ~r"exit") ->
UI.write(:green, "Bye by exlings o/")
System.halt(0)

true ->
wait()
end
end

defp handle_exercise_result(%Exercise{} = e) do
case Exercises.run(e) do
{:stdout, out} -> success(e, out)
Expand Down
6 changes: 5 additions & 1 deletion lib/exlings/exercises.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ defmodule Exlings.Exercises do
Enum.find(list(), &(Exercise.get_state(&1) == :pending))
end

@spec find_by(name: String.t()) :: Exercise.t() | nil
@spec find_by([name: String.t()] | [path: Path.t()]) :: Exercise.t() | nil
def find_by(name: name) do
Enum.find(list(), &(&1.name == name))
end

def find_by(path: path) do
Enum.find(list(), &(&1.path =~ path))
end

@spec run(Exercise.t()) :: {:stdout, binary} | {:stderr, binary} | {:error, binary}
def run(%Exercise{} = e) do
if exec = System.find_executable("elixir") do
Expand Down
32 changes: 32 additions & 0 deletions lib/exlings/file_watcher.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule Exlings.FileWatcher do
@moduledoc false

use GenServer

def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end

@impl true
def init(args) do
priv = List.to_string(:code.priv_dir(:exlings))
path = Path.join(priv, "exercises")
{:ok, watcher} = FileSystem.start_link(dirs: [path], name: :exlings)
FileSystem.subscribe(:exlings)
{:ok, %{watcher: watcher, callback: args[:callback]}}
end

@impl true
def handle_info({:file_event, w, {path, e}}, %{watcher: w} = state) do
case e do
[_, :modified] -> state.callback.(Path.basename(path))

Check warning on line 22 in lib/exlings/file_watcher.ex

View workflow job for this annotation

GitHub Actions / Run tests (25.1.2.1, 1.14)

There should be no unused return values for Path functions.
_ -> nil
end

{:noreply, state}
end

def handle_info({:file_event, w, :stop}, %{watcher: w} = state) do
{:stop, :normal, state}
end
end

0 comments on commit f7fba2e

Please sign in to comment.