Packages

fnord

0.9.11

AI code archaeology

Current section

Files

Jump to
fnord lib settings approvals.ex
Raw

lib/settings/approvals.ex

defmodule Settings.Approvals do
alias Settings.Approvals.RegexMatcher
@type settings :: Settings.t()
@type scope :: :project | :global
@type kind :: binary
@type prefix :: binary
@type subject :: binary
@spec get_approvals(settings, scope) :: map
def get_approvals(settings, :global) do
case Settings.get(settings, "approvals", %{}) do
nil -> %{}
approvals when is_map(approvals) -> approvals
_ -> %{}
end
end
def get_approvals(settings, :project) do
with {:ok, project} <- Settings.get_project(settings) do
case Map.get(project, "approvals", %{}) do
nil -> %{}
approvals when is_map(approvals) -> approvals
_ -> %{}
end
else
_ -> %{}
end
end
@spec get_approvals(settings, scope, kind) :: list(prefix)
def get_approvals(settings, :global, kind) do
settings
|> get_approvals(:global)
|> Map.get(kind, [])
|> validate_approval_list(settings, :global, kind)
end
def get_approvals(settings, :project, kind) do
settings
|> get_approvals(:project)
|> Map.get(kind, [])
|> validate_approval_list(settings, :project, kind)
end
@spec approve(settings, scope, kind, prefix) :: settings
def approve(settings, :global, kind, prefix) do
Settings.update(settings, "approvals", fn
approvals when is_map(approvals) ->
existing = Map.get(approvals, kind, [])
updated =
existing
|> Enum.concat([prefix])
|> Enum.uniq()
|> Enum.sort()
Map.put(approvals, kind, updated)
other ->
UI.warn(
"Settings.Approvals",
"""
FOUND CORRUPTED GLOBAL APPROVALS:
#{inspect(other, limit: :infinity, pretty: true)}
"""
)
%{
kind => [prefix]
}
end)
end
def approve(settings, :project, kind, prefix) do
case Settings.get_project(settings) do
{:ok, %{"name" => name}} ->
# Use Settings.update to modify the nested project data safely
Settings.update(settings, "projects", fn
projects when is_map(projects) ->
project = Map.get(projects, name, %{})
approvals = Map.get(project, "approvals", %{})
existing = Map.get(approvals, kind, [])
updated_prefixes =
existing
|> Enum.concat([prefix])
|> Enum.uniq()
|> Enum.sort()
updated_approvals = Map.put(approvals, kind, updated_prefixes)
updated_project = Map.put(project, "approvals", updated_approvals)
Map.put(projects, name, updated_project)
_ ->
%{
name => %{"approvals" => %{kind => [prefix]}}
}
end)
_ ->
# Check if a project is selected but doesn't have data - create it
case Settings.get_selected_project() do
{:ok, project_name} ->
Settings.update(settings, "projects", fn
projects when is_map(projects) ->
Map.put(projects, project_name, %{
"name" => project_name,
"approvals" => %{kind => [prefix]}
})
_ ->
%{project_name => %{"name" => project_name, "approvals" => %{kind => [prefix]}}}
end)
_ ->
settings
end
end
end
@spec approved?(settings, kind, subject) :: boolean
def approved?(settings, kind, subject) do
# Check both global and project scopes for any matching regex pattern
[:global, :project]
|> Enum.any?(&approved?(settings, &1, kind, subject))
end
def approved?(settings, :global, kind, subject) do
get_approvals(settings, :global, kind)
|> Enum.any?(&RegexMatcher.matches?(&1, subject))
end
def approved?(settings, :project, kind, subject) do
get_approvals(settings, :project, kind)
|> Enum.any?(&RegexMatcher.matches?(&1, subject))
end
@spec prefix_approved?(settings, kind, subject) :: boolean
def prefix_approved?(settings, kind, subject) do
# Check both global and project scopes for any matching prefix
[:global, :project]
|> Enum.any?(&prefix_approved?(settings, &1, kind, subject))
end
def prefix_approved?(settings, :global, kind, subject) do
get_approvals(settings, :global, kind)
|> Enum.any?(&String.starts_with?(subject, &1))
end
def prefix_approved?(settings, :project, kind, subject) do
get_approvals(settings, :project, kind)
|> Enum.any?(&String.starts_with?(subject, &1))
end
# ============================================================================
# Data validation and corruption handling
# ============================================================================
# Validates and repairs corrupted approval lists.
# When approval data is corrupted (e.g., not a list, contains non-strings, or is null),
# this function warns the user and repairs the data by writing the corrected format
# back to the settings file.
@spec validate_approval_list(any, settings, scope, kind) :: list(prefix)
defp validate_approval_list(data, settings, scope, kind) when is_list(data) do
# Filter out non-string entries and warn if any were found
valid_entries = Enum.filter(data, &is_binary/1)
if length(valid_entries) != length(data) do
invalid_count = length(data) - length(valid_entries)
UI.warn(
"Settings.Approvals",
"Found #{invalid_count} invalid entries in #{scope} #{kind} approvals. Cleaning up..."
)
# Fix the corrupted data by re-approving only the valid entries
repair_approval_list(settings, scope, kind, valid_entries)
end
valid_entries
end
defp validate_approval_list(data, settings, scope, kind) when is_nil(data) do
UI.warn(
"Settings.Approvals",
"Found null #{scope} #{kind} approvals. Repairing..."
)
repair_approval_list(settings, scope, kind, [])
[]
end
defp validate_approval_list(data, settings, scope, kind) do
UI.warn(
"Settings.Approvals",
"Found corrupted #{scope} #{kind} approvals (expected list, got #{inspect(data)}). Repairing..."
)
repair_approval_list(settings, scope, kind, [])
[]
end
# Repairs corrupted approval data by writing the correct format to settings.
@spec repair_approval_list(settings, scope, kind, list(prefix)) :: :ok
defp repair_approval_list(settings, :global, kind, valid_entries) do
Settings.update(settings, "approvals", fn approvals ->
approvals = if is_map(approvals), do: approvals, else: %{}
# Get the current list for this kind (may have been updated by another process)
current = Map.get(approvals, kind, [])
# Filter out invalid entries from current list and merge with our valid entries
# Handle case where current is corrupted (not a list)
all_valid =
if is_list(current) do
current
|> Enum.filter(&is_binary/1)
else
[]
end
|> Enum.concat(valid_entries)
|> Enum.uniq()
|> Enum.sort()
Map.put(approvals, kind, all_valid)
end)
:ok
end
defp repair_approval_list(settings, :project, kind, valid_entries) do
with {:ok, %{"name" => name}} <- Settings.get_project(settings) do
Settings.update(settings, "projects", fn projects ->
projects = if is_map(projects), do: projects, else: %{}
project = Map.get(projects, name, %{})
approvals = Map.get(project, "approvals", %{})
# Get the current list for this kind (may have been updated by another process)
current = Map.get(approvals, kind, [])
# Filter out invalid entries from current list and merge with our valid entries
# Handle case where current is corrupted (not a list)
all_valid =
if is_list(current) do
current
|> Enum.filter(&is_binary/1)
else
[]
end
|> Enum.concat(valid_entries)
|> Enum.uniq()
|> Enum.sort()
updated_approvals = Map.put(approvals, kind, all_valid)
updated_project = Map.put(project, "approvals", updated_approvals)
Map.put(projects, name, updated_project)
end)
end
:ok
end
end