Packages
fnord
0.8.65
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/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