Packages

fnord

0.8.87

AI code archaeology

Current section

Files

Jump to
fnord lib memory global.ex
Raw

lib/memory/global.ex

defmodule Memory.Global do
@moduledoc """
Global memory storage implementation for the `Memory` behaviour.
Memories are stored as JSON files in `~/.fnord/memory`.
"""
# ----------------------------------------------------------------------------
# Behaviour Implementation
# ----------------------------------------------------------------------------
@behaviour Memory
@impl Memory
def init() do
with {:ok, _path} <- ensure_storage_path() do
drop_old_storage()
:ok
end
end
@impl Memory
def list() do
with {:ok, files} <- File.ls(storage_path()) do
files
|> Enum.filter(&String.ends_with?(&1, ".json"))
|> Enum.map(fn file ->
file
|> Path.rootname()
|> Memory.slug_to_title()
end)
|> then(&{:ok, &1})
end
end
@impl Memory
def exists?(title) do
title
|> file_path()
|> File.exists?()
end
@impl Memory
def read(title) do
path = file_path(title)
with true <- exists?(title),
{:ok, content} <- read_file(path),
{:ok, memory} <- Memory.unmarshal(content) do
{:ok, memory}
else
false -> {:error, :not_found}
end
end
@impl Memory
def save(memory) do
path = file_path(memory)
with {:ok, json} <- Memory.marshal(memory),
{:ok, _result} <- write_file(path, json) do
:ok
end
end
@impl Memory
def forget(title) do
title
|> file_path()
|> rm_path()
end
@impl Memory
def is_available?(), do: true
# ----------------------------------------------------------------------------
# Internals
# ----------------------------------------------------------------------------
defp storage_path() do
Store.store_home()
|> Path.join("memory")
end
defp old_storage_path() do
Store.store_home()
|> Path.join("memories")
end
defp ensure_storage_path() do
path = storage_path()
with false <- File.exists?(path),
:ok <- File.mkdir_p(path) do
{:ok, path}
else
true -> {:ok, path}
end
end
defp drop_old_storage() do
path = old_storage_path()
if File.exists?(path) do
UI.debug("memory:global", "Removing old global memory storage at #{path}")
File.rm_rf!(path)
end
end
defp file_path(title) when is_binary(title) do
slug = Memory.title_to_slug(title)
Path.join(storage_path(), "#{slug}.json")
end
defp file_path(%Memory{title: title}) do
file_path(title)
end
defp read_file(path) do
case FileLock.with_lock(path, fn -> File.read(path) end) do
{:ok, {:ok, contents}} ->
{:ok, contents}
{:ok, {:error, reason}} ->
{:error, reason}
{:error, reason} ->
{:error, reason}
end
end
defp write_file(path, json) do
FileLock.with_lock(path, fn -> File.write(path, json) end)
end
defp rm_path(path) do
case FileLock.with_lock(path, fn -> File.rm(path) end) do
{:ok, :ok} -> :ok
{:ok, {:error, reason}} -> {:error, reason}
{:error, reason} -> {:error, reason}
{:callback_error, reason} -> {:error, reason}
end
end
end