Packages

fnord

0.9.32

AI code archaeology

Current section

Files

Jump to
fnord lib settings skills.ex
Raw

lib/settings/skills.ex

defmodule Settings.Skills do
@moduledoc """
Manage skill enablement in settings.json.
Schema:
- Global: ["skills"] :: [string]
- Per-project: ["projects", pn, "skills"] :: [string]
## Effective enablement (additive / union)
The effective set of enabled skills is the union of the global list and the
project list (when a project is selected). This matches the semantics used
by `Settings.Frobs`.
All mutations are performed via Settings APIs that provide cross-process locking
and atomic writes.
"""
@type scope :: :global | :project | {:project, String.t()}
@doc """
List enabled skill names for the given scope.
- `:global` reads the top-level `skills` list.
- `:project` reads the selected project's list (or returns `[]` if no project).
- `{:project, name}` reads the named project's list.
This function does not apply override semantics; it reads exactly the requested scope.
"""
@spec list(scope) :: [String.t()]
def list(:global) do
Settings.get(Settings.new(), "skills", [])
|> sanitize_list()
end
def list(:project) do
case Settings.get_selected_project() do
{:ok, pn} -> list({:project, pn})
_ -> []
end
end
def list({:project, pn}) when is_binary(pn) do
settings = Settings.new()
project = Settings.get_project_data(settings, pn) || %{}
Map.get(project, "skills", []) |> sanitize_list()
end
@doc """
Enable a skill in the given scope. Idempotent.
"""
@spec enable(scope, String.t()) :: :ok
def enable(scope, name) when is_binary(name) and byte_size(name) > 0 do
names =
list(scope)
|> Kernel.++([name])
|> sanitize_list()
set_list(scope, names)
end
@doc """
Disable a skill in the given scope. Idempotent.
"""
@spec disable(scope, String.t()) :: :ok
def disable(scope, name) when is_binary(name) do
names =
list(scope)
|> Enum.reject(&(&1 == name))
|> sanitize_list()
set_list(scope, names)
end
@doc """
Return the effective set of enabled skills for the current project context.
Additive semantics: the effective set is the union of the global list and
the project list (when a project is selected).
The return is a `MapSet` for convenient membership checks.
"""
@spec effective_enabled() :: MapSet.t(String.t())
def effective_enabled() do
global = list(:global)
project = list(:project)
(global ++ project)
|> MapSet.new()
end
@doc """
Is the given skill enabled in the current project context?
"""
@spec enabled?(String.t()) :: boolean()
def enabled?(name) when is_binary(name) do
MapSet.member?(effective_enabled(), name)
end
# ----------------------------------------------------------------------------
# Internals
# ----------------------------------------------------------------------------
defp sanitize_list(list) when is_list(list) do
list
|> Enum.filter(&is_binary/1)
|> Enum.uniq()
|> Enum.sort()
end
defp sanitize_list(_), do: []
defp set_list(:global, names) when is_list(names) do
Settings.update(Settings.new(), "skills", fn _ -> names end, [])
:ok
end
defp set_list(:project, names) when is_list(names) do
case Settings.get_selected_project() do
{:ok, pn} -> set_list({:project, pn}, names)
_ -> :ok
end
end
defp set_list({:project, pn}, names) when is_list(names) and is_binary(pn) do
settings = Settings.new()
project = Settings.get_project_data(settings, pn) || %{}
Settings.set_project_data(settings, pn, Map.put(project, "skills", names))
:ok
end
end