aboutsummaryrefslogtreecommitdiff
path: root/src/er_xdg_pipe_menu_cache.erl
blob: a71526e2c828ebf732d6cecd577a6e87c848ee17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
%%%-------------------------------------------------------------------
%% @doc Caches the rendered Openbox pipe menu in memory.
%%
%% Holds the last-rendered menu binary so socket clients get an
%% instant response. `invalidate/0' synchronously rescans the XDG
%% application directories, reparses every `.desktop' file and
%% re-renders the menu, replacing the cached binary.
%% @end
%%%-------------------------------------------------------------------

-module(er_xdg_pipe_menu_cache).

-behaviour(gen_server).

-export([start_link/0, get_menu/0, invalidate/0, build/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).

-spec start_link() -> {ok, pid()}.
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

-spec get_menu() -> binary().
get_menu() ->
    gen_server:call(?MODULE, get_menu).

-spec invalidate() -> ok.
invalidate() ->
    gen_server:cast(?MODULE, invalidate).

%% Pure pipeline: explicit dirs in, rendered menu binary out. Kept
%% side-effect-free (besides reading the filesystem) and exported so
%% it can be unit tested directly and reused by the CLI client's
%% no-daemon fallback, without going through the gen_server at all.
-spec build([file:filename_all()]) -> binary().
build(Dirs) ->
    Files = er_xdg_pipe_menu_scanner:find_desktop_files(Dirs),
    Entries = lists:filtermap(fun parse/1, Files),
    er_xdg_pipe_menu_renderer:render(Entries).

parse(File) ->
    case er_xdg_pipe_menu_desktop_entry:parse_file(File) of
        {ok, Entry} -> {true, Entry};
        {error, _} -> false
    end.

init([]) ->
    {ok, build(er_xdg_pipe_menu_scanner:data_dirs())}.

handle_call(get_menu, _From, Menu) ->
    {reply, Menu, Menu}.

handle_cast(invalidate, _Menu) ->
    {noreply, build(er_xdg_pipe_menu_scanner:data_dirs())}.

handle_info(_Info, State) ->
    {noreply, State}.