defmodule ExTTRPGDev.RuleSystems do
alias ExTTRPGDev.RuleSystems
alias ExTTRPGDev.Globals
alias ExTTRPGDev.RuleSystems.RuleSystem
@moduledoc """
Module which enables interactions with the varying defined systems in the
system_configs. Basically sytem_configs define what systems are available and
how they should be interpreted, and this module is is the beginning of the
interpretation.
"""
@doc """
List the systems available
## Examples
iex> ExTTRPGDev.RuleSystems.list_systems()
["dnd_5e_srd"]
"""
def list_systems do
list_bundled_systems() ++ list_local_systems()
end
@doc """
List the ExTTRPGDev local custom systems available
## Examples
iex> ExTTRPGDev.RuleSystems.list_bundled_systems()
["dnd_5e_srd"]
"""
def list_bundled_systems do
File.ls!(Globals.system_configs_path())
end
@doc """
List the ExTTRPGDev bundled systems available
## Examples
iex> ExTTRPGDev.RuleSystems.list_local_systems()
[]
"""
def list_local_systems do
if File.exists?(Globals.local_system_configs_path()) do
File.ls!(Globals.local_system_configs_path())
else
[]
end
end
@doc """
Checks if the given system is a bundled system
## Examples
iex> ExTTRPGDev.RuleSystems.is_bundled_system?("dnd_5e_srd")
true
iex> ExTTRPGDev.RuleSystems.is_bundled_system?("my_custom_rule_system")
false
"""
def is_bundled_system?(system) when is_bitstring(system) do
list_bundled_systems()
|> Enum.any?(fn configured_system -> configured_system == system end)
end
@doc """
Checks if the given system is a local system
## Examples
iex> ExTTRPGDev.RuleSystems.is_local_system?("dnd_5e_srd")
false
iex> ExTTRPGDev.RuleSystems.is_local_system?("my_custom_rule_system")
true
"""
def is_local_system?(system) when is_bitstring(system) do
list_local_systems()
|> Enum.any?(fn configured_system -> configured_system == system end)
end
@doc """
Checks if the given system is configured.
Returns true if system is configured, otherwise false.
## 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
list_systems()
|> Enum.any?(fn configured_systems -> configured_systems == system end)
end
@doc """
Ensures a system is configured. If the system is configured, the system name
is returned. If the system isn't configured, an exception is raised.
## 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 congifured
"""
def assert_configured!(system) when is_bitstring(system) do
if RuleSystems.is_configured?(system) do
system
else
raise "System `#{system}` is not congifured"
end
end
@doc """
Returns the path to to the systems config directory
## Examples
iex> ExTTRPGDev.RuleSystems.system_path!("dnd_5e_srd")
"/full/path/to/project/ex_ttrpg_dev/system_configs/dnd_5e_srd"
"""
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 """
Reads in all of the JSON files for the specified and decodes
the json into a %Systems{} struct
## Examples
iex> ExTTRPGDev.RuleSystems.load_system!("dnd_5e_srd")
%ExTTRPGDev.RuleSystems.RuleSystem{}
"""
def load_system!(system) when is_bitstring(system) do
system_path = ExTTRPGDev.RuleSystems.system_path!(system)
File.ls!(system_path)
|> Enum.filter(fn file_name -> Regex.match?(Globals.json_file_pattern(), file_name) end)
|> Enum.reduce(%{}, fn file, acc ->
Path.join(system_path, file)
|> File.read!()
|> Poison.decode!()
|> Map.merge(acc)
end)
|> Poison.encode!()
|> RuleSystem.from_json!()
end
@doc """
Saves the system specification locally as a JSON file. If a system is a
bundled config, an exception is raised. If the system already exists and
`overwrite` wasn't specificed as true, an exception is raised.
## Examples
iex> ExTTRPGDev>RuleSystems.save_system!(%ExTTRPGDev.RuleSystems.RuleSystem{})
:ok
iex> ExTTRPGDev>RuleSystems.save_system!(%ExTTRPGDev.RuleSystems.RuleSystem{})
:error, :config_already_exists
iex> ExTTRPGDev>RuleSystems.save_system!(%ExTTRPGDev.RuleSystems.RuleSystem{}, true)
:ok
"""
def save_system!(
%RuleSystem{metadata: %RuleSystems.Metadata{slug: system_slug}} = system,
overwrite \\ false
) do
cond do
is_bundled_system?(system_slug) ->
raise "System `#{system_slug}` is a bundled config. Please change system's name and slug before saving."
not is_local_system?(system_slug) or overwrite ->
system_path = system_path!(system_slug)
# if `overwrite` delete system dir
# also... this seems incredibly dangerous
if overwrite do
File.rm_rf!(system_path)
end
# create the dir if it doesn't exist
File.mkdir_p!(system_path)
# write the system config to file
File.write!(Path.join(system_path, "system.json"), Poison.encode!(system), [:binary])
true ->
raise "System already exists. To overwrite, pass `overwrite` as true"
end
end
end