Packages

fnord

0.6.3

AI code archaeology

Current section

Files

Jump to
fnord lib store project entry.ex
Raw

lib/store/project/entry.ex

defmodule Store.Project.Entry do
defstruct [
:project,
:file,
:rel_path,
:key,
:store_path,
:metadata,
:summary,
:outline,
:embeddings
]
@type t :: %__MODULE__{}
def new_from_file_path(project, file) do
abs_path = Store.Project.expand_path(file, project)
rel_path = Store.Project.relative_path(abs_path, project)
key = key(abs_path)
store_path = Path.join(project.store_path, key)
metadata = Store.Project.Entry.Metadata.new(store_path, abs_path)
summary = Store.Project.Entry.Summary.new(store_path, abs_path)
outline = Store.Project.Entry.Outline.new(store_path, abs_path)
embeddings = Store.Project.Entry.Embeddings.new(store_path, abs_path)
%__MODULE__{
project: project,
file: abs_path,
rel_path: rel_path,
key: key,
store_path: store_path,
metadata: metadata,
summary: summary,
outline: outline,
embeddings: embeddings
}
end
def new_from_entry_path(project, entry_path) do
file_path =
entry_path
|> Path.join("metadata.json")
|> File.read!()
|> Jason.decode!()
|> Map.get("file")
new_from_file_path(project, file_path)
end
def source_file_exists?(entry) do
cond do
!File.exists?(entry.file) -> false
Git.is_ignored?(entry.file) -> false
true -> true
end
end
def exists_in_store?(entry) do
File.dir?(entry.store_path)
end
def is_git_ignored?(entry) do
Git.is_ignored?(entry.file)
end
def create(entry), do: File.mkdir_p!(entry.store_path)
def delete(entry), do: File.rm_rf!(entry.store_path)
def is_incomplete?(entry) do
cond do
!has_metadata?(entry) -> true
!has_summary?(entry) -> true
!has_outline?(entry) -> true
!has_embeddings?(entry) -> true
true -> false
end
end
def hash_is_current?(entry) do
with {:ok, hash} <- get_last_hash(entry) do
hash == file_sha256(entry.file)
else
_ -> false
end
end
def is_stale?(entry) do
cond do
# Never indexed
!exists_in_store?(entry) -> true
# Files that are missing or have missing indexes must be reindexed
is_incomplete?(entry) -> true
# File contents have changed
!hash_is_current?(entry) -> true
true -> false
end
end
def read_source_file(entry), do: File.read(entry.file)
def read(entry) do
with {:ok, metadata} <- read_metadata(entry),
{:ok, summary} <- read_summary(entry),
{:ok, outline} <- read_outline(entry),
{:ok, embeddings} <- read_embeddings(entry) do
info =
metadata
|> Map.put("summary", summary)
|> Map.put("outline", outline)
|> Map.put("embeddings", embeddings)
{:ok, info}
end
end
def save(entry, summary, outline, embeddings) do
delete(entry)
create(entry)
with :ok <- save_metadata(entry),
:ok <- save_summary(entry, summary),
:ok <- save_outline(entry, outline),
:ok <- save_embeddings(entry, embeddings) do
:ok
end
end
# -----------------------------------------------------------------------------
# metadata.json
# -----------------------------------------------------------------------------
def metadata_file_path(entry), do: Store.Project.Entry.Metadata.store_path(entry.metadata)
def has_metadata?(entry), do: Store.Project.Entry.Metadata.exists?(entry.metadata)
def read_metadata(entry), do: Store.Project.Entry.Metadata.read(entry.metadata)
def save_metadata(entry), do: Store.Project.Entry.Metadata.write(entry.metadata, nil)
# -----------------------------------------------------------------------------
# summary
# -----------------------------------------------------------------------------
def summary_file_path(entry), do: Store.Project.Entry.Summary.store_path(entry.summary)
def has_summary?(entry), do: Store.Project.Entry.Summary.exists?(entry.summary)
def read_summary(entry), do: Store.Project.Entry.Summary.read(entry.summary)
def save_summary(entry, data), do: Store.Project.Entry.Summary.write(entry.summary, data)
# -----------------------------------------------------------------------------
# outline
# -----------------------------------------------------------------------------
def outline_file_path(entry), do: Store.Project.Entry.Outline.store_path(entry.outline)
def has_outline?(entry), do: Store.Project.Entry.Outline.exists?(entry.outline)
def read_outline(entry), do: Store.Project.Entry.Outline.read(entry.outline)
def save_outline(entry, data), do: Store.Project.Entry.Outline.write(entry.outline, data)
# -----------------------------------------------------------------------------
# embeddings
# -----------------------------------------------------------------------------
def embeddings_file_paths(entry),
do: Store.Project.Entry.Embeddings.store_path(entry.embeddings)
def has_embeddings?(entry), do: Store.Project.Entry.Embeddings.exists?(entry.embeddings)
def read_embeddings(entry), do: Store.Project.Entry.Embeddings.read(entry.embeddings)
def save_embeddings(entry, embeddings),
do: Store.Project.Entry.Embeddings.write(entry.embeddings, embeddings)
# -----------------------------------------------------------------------------
# Private functions
# -----------------------------------------------------------------------------
defp get_last_hash(entry) do
with {:ok, metadata} <- read_metadata(entry) do
Map.fetch(metadata, "hash")
end
end
defp key(file_path), do: sha256(file_path)
defp file_sha256(file_path) do
case File.read(file_path) do
{:ok, content} -> sha256(content)
{:error, reason} -> {:error, reason}
end
end
defp sha256(content) do
:crypto.hash(:sha256, content)
|> Base.encode16(case: :lower)
end
end