This repository has been archived by the owner on Jul 6, 2023. It is now read-only.
/
error_cache.ex
122 lines (97 loc) · 2.68 KB
/
error_cache.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
defmodule Still.Compiler.ErrorCache do
@moduledoc """
Saves an error occurring within a file's compilation and allows them to be
retrieved for a prettified display.
Since files are compiled asynchronously, the browser (or other interested
parties) require a centralised access to compilation errors.
"""
use GenServer
alias Still.SourceFile
import Still.Utils
def start_link(_) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
@doc """
Save the given error or clear it if the compilation was successful.
If `result` is `{:ok, source_file}`, the error cache for the given file is
cleared. Otherwise it is updated.
"""
def set(result) do
GenServer.cast(__MODULE__, {:set, result})
end
@doc """
Clears the saved errors for the given input file.
"""
def clear(input_file) do
GenServer.cast(__MODULE__, {:clear, input_file})
end
@doc """
Clears the errors for all files. This function is dangerous and should be
only used for debugging and testing purposes.
"""
def clear do
GenServer.cast(__MODULE__, :clear)
end
@doc """
Retrieve all saved errors, for all files.
"""
def get_errors do
GenServer.call(__MODULE__, :get_errors)
end
@impl true
def init(_) do
{:ok, %{errors: %{}}}
end
@impl true
def handle_call(:get_errors, _, state) do
{:reply, state.errors, state}
end
def handle_cast({:set, {:ok, source_file}}, state) do
{errors, previous_errors} =
state.errors
|> Enum.split_with(fn {_, error} ->
error.source_file.input_file != source_file.input_file
end)
recompile_source_files(previous_errors)
state = %{state | errors: Enum.into(errors, %{})}
{:noreply, state}
end
def handle_cast({:set, {:error, error}}, state) do
errors =
state.errors
|> Map.put(source_file_id(error.source_file), error)
state = %{state | errors: errors}
{:noreply, state}
end
@impl true
def handle_cast({:clear, input_file}, state) do
{:noreply, state |> Map.delete(input_file)}
end
@impl true
def handle_cast(:clear, _state) do
{:noreply, %{errors: %{}}}
end
def handle_cast(_, state) do
{:noreply, state}
end
@impl true
def handle_info(_, state) do
{:noreply, state}
end
defp source_file_id(%SourceFile{dependency_chain: dependency_chain}) do
dependency_chain
|> Enum.join(" <- ")
end
defp recompile_source_files([]), do: nil
defp recompile_source_files(errors) do
errors
|> Enum.map(fn {_, error} ->
Task.async(fn ->
error.source_file.dependency_chain
|> List.last()
|> compile_file_metadata()
end)
end)
|> Task.await_many(:infinity)
end
end