Current section

Files

Jump to
hecate_sdk src hecate_plugin_paths.erl
Raw

src/hecate_plugin_paths.erl

%%%-------------------------------------------------------------------
%%% @doc Standard directory layout for plugin data.
%%%
%%% Replaces per-plugin path modules (e.g., app_scribed_paths.erl).
%%% All plugins get the same layout under their data directory:
%%%
%%% ~/.hecate/plugins/{plugin_name}/
%%% ├── sqlite/ — SQLite read model databases
%%% ├── reckon-db/ — ReckonDB event store
%%% └── run/ — Runtime files (PID, etc.)
%%%
%%% Resolution order for the base directory:
%%% 1. application:get_env(hecate, data_dir) — set when running inside hecate-daemon
%%% 2. HECATE_HOME env var — set when running as a standalone plugin
%%% 3. ~/.hecate — default fallback
%%% @end
%%%-------------------------------------------------------------------
-module(hecate_plugin_paths).
-export([base_dir/1, sqlite_dir/1, sqlite_path/2, reckon_dir/1,
reckon_path/2, run_dir/1, run_path/2, ensure_layout/1]).
%% @doc Root data directory for a plugin.
-spec base_dir(PluginName :: string() | binary()) -> file:filename().
base_dir(PluginName) ->
Name = sanitize_name(PluginName),
HecateHome = hecate_home(),
filename:join([HecateHome, "plugins", Name]).
%% @doc SQLite directory for a plugin's read models.
-spec sqlite_dir(string() | binary()) -> file:filename().
sqlite_dir(PluginName) ->
filename:join(base_dir(PluginName), "sqlite").
%% @doc Full path to a named SQLite database file.
-spec sqlite_path(string() | binary(), string()) -> file:filename().
sqlite_path(PluginName, DbName) ->
filename:join(sqlite_dir(PluginName), DbName).
%% @doc ReckonDB directory for a plugin's event store.
-spec reckon_dir(string() | binary()) -> file:filename().
reckon_dir(PluginName) ->
filename:join(base_dir(PluginName), "reckon-db").
%% @doc Full path to a named ReckonDB store directory.
-spec reckon_path(string() | binary(), string()) -> file:filename().
reckon_path(PluginName, StoreName) ->
filename:join(reckon_dir(PluginName), StoreName).
%% @doc Runtime directory for PID files, temp data.
-spec run_dir(string() | binary()) -> file:filename().
run_dir(PluginName) ->
filename:join(base_dir(PluginName), "run").
%% @doc Full path to a named runtime file.
-spec run_path(string() | binary(), string()) -> file:filename().
run_path(PluginName, FileName) ->
filename:join(run_dir(PluginName), FileName).
%% @doc Create all standard directories for a plugin.
-spec ensure_layout(string() | binary()) -> ok.
ensure_layout(PluginName) ->
Dirs = [sqlite_dir(PluginName), reckon_dir(PluginName), run_dir(PluginName)],
lists:foreach(fun(Dir) -> ok = filelib:ensure_path(Dir) end, Dirs).
%%--------------------------------------------------------------------
%% Internal
%%--------------------------------------------------------------------
hecate_home() ->
case application:get_env(hecate, data_dir) of
{ok, Dir} -> expand_tilde(Dir);
undefined -> hecate_home_from_env()
end.
hecate_home_from_env() ->
case os:getenv("HECATE_HOME") of
false -> filename:join(os:getenv("HOME"), ".hecate");
Dir -> Dir
end.
expand_tilde([$~ | Rest]) -> os:getenv("HOME") ++ Rest;
expand_tilde(Path) -> Path.
%% Strip org prefix (e.g., "hecate-apps/scribe" -> "scribe")
sanitize_name(Name) when is_binary(Name) ->
case binary:split(Name, <<"/">>) of
[_, Short] -> binary_to_list(Short);
[Single] -> binary_to_list(Single)
end;
sanitize_name(Name) when is_list(Name) ->
Name.