Current section
Files
Jump to
Current section
Files
lib/rule_systems.ex
defmodule ExTTRPGDev.RuleSystems do
@moduledoc """
Public API for interacting with rule systems.
Rule systems are defined as directories of TOML files under `priv/system_configs/`
(bundled) or `local_system_configs/` (user-defined). Each system directory must
contain a `module.toml` manifest.
"""
alias ExTTRPGDev.Globals
alias ExTTRPGDev.RuleSystem.{Graph, Loader}
alias ExTTRPGDev.RuleSystems.LoadedSystem
@doc """
Lists all available systems (bundled + local).
## Examples
iex> ExTTRPGDev.RuleSystems.list_systems() |> Enum.member?("dnd_5e_srd")
true
"""
def list_systems do
list_bundled_systems() ++ list_local_systems()
end
@doc """
Lists bundled systems shipped with the library.
## Examples
iex> ExTTRPGDev.RuleSystems.list_bundled_systems() |> Enum.member?("dnd_5e_srd")
true
"""
def list_bundled_systems do
base = Globals.system_configs_path()
File.ls!(base)
|> Enum.filter(fn name ->
File.dir?(Path.join(base, name)) and
File.exists?(Path.join([base, name, "module.toml"]))
end)
end
@doc """
Lists user-defined local systems.
## Examples
iex> ExTTRPGDev.RuleSystems.list_local_systems() |> is_list()
true
"""
def list_local_systems do
base = Globals.local_system_configs_path()
if File.exists?(base) do
File.ls!(base)
|> Enum.filter(fn name ->
File.dir?(Path.join(base, name)) and
File.exists?(Path.join([base, name, "module.toml"]))
end)
else
[]
end
end
@doc """
Returns true if the system is a bundled system.
## Examples
iex> ExTTRPGDev.RuleSystems.is_bundled_system?("dnd_5e_srd")
true
iex> ExTTRPGDev.RuleSystems.is_bundled_system?("non_existent_system")
false
"""
def is_bundled_system?(system) when is_bitstring(system) do
Enum.any?(list_bundled_systems(), &(&1 == system))
end
@doc """
Returns true if the system is a user-defined local system.
## Examples
iex> ExTTRPGDev.RuleSystems.is_local_system?("dnd_5e_srd")
false
iex> ExTTRPGDev.RuleSystems.is_local_system?("non_existent_system")
false
"""
def is_local_system?(system) when is_bitstring(system) do
Enum.any?(list_local_systems(), &(&1 == system))
end
@doc """
Returns true if the system is configured (bundled or local).
## Examples
iex> ExTTRPGDev.RuleSystems.is_configured?("dnd_5e_srd")
true
iex> ExTTRPGDev.RuleSystems.is_configured?("non_existent_system")
false
"""
def is_configured?(system) when is_bitstring(system) do
Enum.any?(list_systems(), &(&1 == system))
end
@doc """
Ensures a system is configured, returning the slug if so, raising if not.
## Examples
iex> ExTTRPGDev.RuleSystems.assert_configured!("dnd_5e_srd")
"dnd_5e_srd"
iex> ExTTRPGDev.RuleSystems.assert_configured!("not_configured")
** (RuntimeError) System `not_configured` is not configured
"""
def assert_configured!(system) when is_bitstring(system) do
if is_configured?(system) do
system
else
raise "System `#{system}` is not configured"
end
end
@doc """
Returns the filesystem path to the given system's directory.
## Examples
iex> ExTTRPGDev.RuleSystems.system_path!("dnd_5e_srd") |> String.ends_with?("dnd_5e_srd")
true
"""
def system_path!(system) when is_bitstring(system) do
if is_bundled_system?(system) do
Path.join(Globals.system_configs_path(), system)
else
Path.join(Globals.local_system_configs_path(), system)
end
end
@doc """
Loads, parses, and validates a rule system by slug.
Returns a `%LoadedSystem{}` containing the parsed module, validated DAG,
and all supporting data needed for evaluation.
## Examples
iex> ExTTRPGDev.RuleSystems.load_system!("dnd_5e_srd") |> is_struct(ExTTRPGDev.RuleSystems.LoadedSystem)
true
"""
def load_system!(system) when is_bitstring(system) do
path = system_path!(system)
loader_data = Loader.load!(path)
case Graph.build(loader_data) do
{:ok, system_map} ->
%LoadedSystem{
module: loader_data.module,
graph: system_map.graph,
nodes: system_map.nodes,
rolling_methods: loader_data.rolling_methods,
concept_metadata: loader_data.concept_metadata,
effects: loader_data.effects,
inventory_rules: loader_data.inventory_rules
}
{:error, reason} ->
raise "Failed to build rule system graph for #{system}: #{inspect(reason)}"
end
end
end