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