Packages
fnord
0.8.34
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/services/task.ex
defmodule Services.Task do
defstruct [
:next_id,
:lists
]
@type list_id :: non_neg_integer
@type task_id :: binary
@type task_data :: any
@type task_result :: any
@type task :: %{
id: task_id,
outcome: :todo | :done | :failed,
data: task_data,
result: task_result | nil
}
use GenServer
# ----------------------------------------------------------------------------
# Client API
# ----------------------------------------------------------------------------
@spec start_link(any) :: GenServer.on_start()
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@spec start_list() :: list_id
def start_list() do
GenServer.call(__MODULE__, :start_list)
end
@spec get_list(list_id) :: [task] | {:error, :not_found}
def get_list(list_id) do
GenServer.call(__MODULE__, {:get_list, list_id})
end
@spec add_task(list_id, task_id, task_data) :: :ok
def add_task(list_id, task_id, task_data) do
GenServer.cast(__MODULE__, {:add_task, list_id, task_id, task_data})
end
@spec complete_task(list_id, task_id, task_result) :: :ok
def complete_task(list_id, task_id, result) do
GenServer.cast(__MODULE__, {:complete_task, list_id, task_id, :done, result})
end
@spec fail_task(list_id, task_id, task_result) :: :ok
def fail_task(list_id, task_id, msg) do
GenServer.cast(__MODULE__, {:complete_task, list_id, task_id, :failed, msg})
end
@spec push_task(list_id, task_id, task_data) :: :ok
def push_task(list_id, task_id, task_data) do
GenServer.cast(__MODULE__, {:push_task, list_id, task_id, task_data})
end
@spec peek_task(list_id) :: {:ok, task} | {:error, :not_found} | {:error, :empty}
def peek_task(list_id) do
GenServer.call(__MODULE__, {:peek_task, list_id})
end
@spec as_string(list_id | list(task), boolean) :: binary
def as_string(subject, detail? \\ false)
def as_string(list_id, detail?) when is_integer(list_id) do
list_id
|> get_list()
|> case do
{:error, :not_found} -> "List #{list_id} not found"
tasks -> as_string(tasks, detail?)
end
end
def as_string([], _), do: ""
def as_string([task | remaining], detail?) do
[
as_string(task, detail?),
as_string(remaining, detail?)
]
|> Enum.join("\n")
end
def as_string(%{id: id, outcome: :done, result: result}, true) do
"[✓] #{id}: #{result}"
end
def as_string(%{id: id, outcome: :failed, result: result}, true) do
"[✗] #{id}: #{result}"
end
def as_string(%{id: id, outcome: :todo}, _), do: "[ ] #{id}"
def as_string(%{id: id, outcome: :done}, _), do: "[✓] #{id}"
def as_string(%{id: id, outcome: :failed}, _), do: "[✗] #{id}"
# ----------------------------------------------------------------------------
# Server Callbacks
# ----------------------------------------------------------------------------
def init(_) do
{:ok, %__MODULE__{next_id: 1, lists: %{}}}
end
def handle_call(:start_list, _from, %{lists: lists, next_id: next_id} = state) do
{:reply, next_id,
%{
state
| lists: Map.put(lists, next_id, []),
next_id: next_id + 1
}}
end
def handle_call({:get_list, list_id}, _from, state) do
state.lists
|> Map.get(list_id)
|> case do
nil ->
{:reply, {:error, :not_found}, state}
tasks ->
tasks
|> Enum.reverse()
|> then(&{:reply, &1, state})
end
end
def handle_call({:peek_task, list_id}, _from, state) do
case Map.fetch(state.lists, list_id) do
:error ->
{:reply, {:error, :not_found}, state}
{:ok, tasks} ->
tasks
|> Enum.reverse()
|> Enum.find(fn task -> task.outcome == :todo end)
|> case do
nil -> {:reply, {:error, :empty}, state}
task -> {:reply, {:ok, task}, state}
end
end
end
def handle_cast({:add_task, list_id, task_id, task_data}, state) do
# Silently ignore operations on nonexistent lists
case Map.fetch(state.lists, list_id) do
:error ->
{:noreply, state}
{:ok, tasks} ->
task = %{
id: task_id,
data: task_data,
outcome: :todo,
result: nil
}
new_list = [task | tasks]
{:noreply, %{state | lists: Map.put(state.lists, list_id, new_list)}}
end
end
def handle_cast({:complete_task, list_id, task_id, outcome, result}, state) do
state
|> Map.get(:lists, %{})
|> Map.fetch(list_id)
|> case do
:error ->
{:noreply, state}
{:ok, tasks} ->
tasks
|> Enum.map(fn
%{id: ^task_id} = task -> %{task | outcome: outcome, result: result}
task -> task
end)
|> then(&{:noreply, %{state | lists: %{state.lists | list_id => &1}}})
end
end
def handle_cast({:push_task, list_id, task_id, task_data}, state) do
case Map.fetch(state.lists, list_id) do
:error ->
{:noreply, state}
{:ok, tasks} ->
task = %{
id: task_id,
data: task_data,
outcome: :todo,
result: nil
}
new_list = tasks ++ [task]
{:noreply, %{state | lists: %{state.lists | list_id => new_list}}}
end
end
def handle_cast({:drop_task, list_id}, state) do
state.lists
|> Map.fetch(list_id)
|> case do
:error -> {:noreply, state}
{:ok, []} -> {:noreply, state}
{:ok, [_head | tail]} -> {:noreply, %{state | lists: %{state.lists | list_id => tail}}}
end
end
end