Current section

Files

Jump to
spectra src spectra_module_types.erl
Raw

src/spectra_module_types.erl

-module(spectra_module_types).
-export([get/1, clear/1]).
%% Meant to be used when doing manual testing.
-ignore_xref([clear/1]).
-type module_version() :: term().
-define(APPLICATION, spectra).
%% API
-spec get(Module :: module()) -> spectra:type_info().
get(Module) ->
case application:get_env(?APPLICATION, use_module_types_cache, false) of
true ->
{ok, Vsn} = module_vsn(Module),
case pers_type(Module) of
{Vsn, TypeInfo} ->
TypeInfo;
undefined ->
TypeInfo = spectra_abstract_code:types_in_module(Module),
pers_types_set(Module, Vsn, TypeInfo),
TypeInfo
end;
false ->
spectra_abstract_code:types_in_module(Module)
end.
-spec clear(Module :: module()) -> ok.
clear(Module) ->
persistent_term:erase({?MODULE, pers_types, Module}),
ok.
%% INTERNAL
-spec pers_type(Module :: module()) ->
{module_version(), spectra:type_info()} | undefined.
pers_type(Module) ->
persistent_term:get({?MODULE, pers_types, Module}, undefined).
-spec pers_types_set(
Module :: module(),
Vsn :: module_version(),
TypeInfo :: spectra:type_info()
) ->
ok.
pers_types_set(Module, Vsn, TypeInfo) ->
persistent_term:put({?MODULE, pers_types, Module}, {Vsn, TypeInfo}).
ensure_module(Module) ->
erlang:module_loaded(Module) orelse code:which(Module) =/= non_existing.
-spec module_vsn(Module :: module()) ->
{ok, Version :: module_version()} | {error, [spectra:error()]}.
module_vsn(Module) ->
case ensure_module(Module) of
true ->
case erlang:get_module_info(Module, attributes) of
Attrs when is_list(Attrs) ->
{vsn, Vsn} = lists:keyfind(vsn, 1, Attrs),
{ok, Vsn}
end;
false ->
erlang:error({module_types_not_found, Module, non_existing})
end.