Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
elixir 1.13.2-otp-24
erlang 24.0.5
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ File.write!("done.txt", Enum.join(Enum.map(done, &Todo.to_string/1), "\n"))
## Roadmap
- Add a File Watcher for Local todo.txt
- Add a File Watcher for Google Drive todo.txt
- Add Vapor for starting application with config file or environment variable
24 changes: 19 additions & 5 deletions lib/todo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,24 @@ defmodule Todo do
iex> Todo.parse("task meta:data meta1:data1")
%Todo{description: "task", additional_fields: %{"meta" => "data", "meta1" => "data1"}}

iex> Todo.parse("task meta:data meta1:data1 @context")
%Todo{description: "task @context", contexts: [:context], additional_fields: %{"meta" => "data", "meta1" => "data1"}}

iex> Todo.parse("task due:2021-09-13 meta:data meta1:data1")
%Todo{description: "task", additional_fields: %{"meta" => "data", "meta1" => "data1"}, due_date: ~D[2021-09-13]}

"""
def parse(str) do
case parser(str) do
{:ok, parsed, "", _, _, _} -> Enum.reduce(parsed, %Todo{}, &set_from_parsed/2)
{:error, message, _, _, _, _} -> message
{:ok, parsed, "", _, _, _} ->
Enum.reduce(parsed, %Todo{}, &set_from_parsed/2)

{:ok, parsed, additional_description, _, _, _} ->
todo = Enum.reduce(parsed, %Todo{}, &set_from_parsed/2)
set_from_parsed({:description, additional_description}, todo)

{:error, message, _, _, _, _} ->
message
end
end

Expand Down Expand Up @@ -294,6 +304,8 @@ defmodule Todo do
end

defp set_from_parsed({:description, description}, todo) do
%Todo{description: d, contexts: c, projects: p} = todo

contexts =
case context_parser(description) do
{:ok, contexts, _, _, _, _} -> contexts
Expand All @@ -306,9 +318,11 @@ defmodule Todo do
{:error, message, _, _, _, _} -> {:error, message}
end

Map.put(todo, :description, description)
|> Map.put(:contexts, contexts)
|> Map.put(:projects, projects)
new_descriptions = String.trim(d <> description)

Map.put(todo, :description, new_descriptions)
|> Map.put(:contexts, c ++ contexts)
|> Map.put(:projects, p ++ projects)
end

defp set_from_parsed({:done, done}, todo) do
Expand Down
21 changes: 21 additions & 0 deletions lib/todo_txt/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule TodoTxt.Application do
@moduledoc false

use Application

def start(_type, args) do
# TODO: figure this out
# Set default paths if none are passed
todo_txt_file_path = get_in(args, [Access.key(:todo_txt_file_path, "$TODO_DIR/todo.txt")])
done_txt_file_path = get_in(args, [Access.key(:done_txt_file_path, "$TODO_DIR/done.txt")])

children = [
{TodoTxt.Server,
todo_txt_file_path: todo_txt_file_path, done_txt_file_path: done_txt_file_path}
# TODO: Add file_system watcher here
]

opts = [strategy: :one_for_one, name: VaporExample.Supervisor]
Supervisor.start_link(children, opts)
end
end
25 changes: 25 additions & 0 deletions lib/todo_txt/server.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule TodoTxt.Server do
@moduledoc """
GenServer that allows interaction with a Todo.txt and optionally a Done.txt file
"""

use GenServer

alias TodoTxt.State

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

def todos do
GenServer.call(__MODULE__, :todos)
end

@impl true
def init(state), do: {:ok, state}
# TODO: Add file watcher

@impl true
def handle_call(:todos, _from, %State{} = state) do
reloaded_state = State.load_todos(state)
{:reply, Map.get(reloaded_state, :todos), reloaded_state}
end
end
51 changes: 51 additions & 0 deletions lib/todo_txt/state.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule TodoTxt.State do
alias TodoTxt.State

defstruct todo_txt_file_path: "#{System.get_env("TODO_DIR")}/todo.txt",
done_txt_file_path: "#{System.get_env("TODO_DIR")}/done.txt",
file_location: :local,
options: [],
todos: [],
history: :none

def new(state = %State{}) do
load_todos(state)
end

defp validate(state) do
%State{
todo_txt_file_path: todo_txt_file_path,
done_txt_file_path: done_txt_file_path,
file_location: file_location
} = state

case file_location do
:local ->
cond do
!File.exists?(todo_txt_file_path) ->
{:error, "File #{todo_txt_file_path} does not exist"}

done_txt_file_path != :none && !File.exists?(done_txt_file_path) ->
{:error, "File #{done_txt_file_path} does not exist"}

true ->
{:ok, state}
end

invalid_location ->
{:error, "#{invalid_location} is not a valid file_location"}
end
end

def load_todos(state) do
{:ok, %State{todo_txt_file_path: todo_txt_file_path}} = validate(state)

todos =
todo_txt_file_path
|> File.read!()
|> String.split("\n", trim: true)
|> Enum.map(&Todo.parse/1)

Map.put(state, :todos, todos)
end
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ defmodule TodoTxt.MixProject do
{:dialyxir, "~> 1.0.0", only: :dev, runtime: false},
{:ex_doc, "~> 0.23.0", only: :dev, runtime: false},
{:excoveralls, "~> 0.13.0", only: :test},
{:file_system, "~> 0.2"},
{:git_hooks, "~> 0.5.2", only: :dev, runtime: false},
{:mix_test_watch, "~> 1.0.2", only: :dev, runtime: false},
{:nimble_parsec, "~> 1.1.0"},
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/todo_txts/done.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
x 2021-05-11 Print out National Park Permit +SanJacintoTrip
x 2022-07-20 2022-07-19 Flush water heater @home pri:A
x 2022-06-27 2022-06-27 go through @work email
x 2022-06-27 2022-04-15 Organize ideas section of Proposals @work
8 changes: 8 additions & 0 deletions test/fixtures/todo_txts/todo.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(A) Call Mom @Phone +Family due:2022-02-01
2022-03-14 (A) Schedule annual checkup +Health
(B) Outline chapter 5 +Novel @Computer (#pomo: 4/20)
(C) Add cover sheets @Office +TPSReports
Plan backyard herb garden @Home
Pick up milk @GroceryStore
Research self-publishing services +Novel @Computer url:https://www.selfpublishingservices.com
x Download Todo.txt mobile app @Phone
10 changes: 10 additions & 0 deletions test/todo_txt/server_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule TodoTxt.ServerTest do
use ExUnit.Case
doctest TodoTxt.Server

describe "start_link/1" do
test "accepts a TodoTxt.State on start" do
assert {:ok, _pid} = TodoTxt.Server.start_link(%TodoTxt.State{})
end
end
end