-module(spectra_type). -include("../include/spectra_internal.hrl"). -export([ can_be_missing/2, type_reference/1, get_meta/1, set_meta/2, add_doc_to_type/2, normalize_doc/1, normalize_function_doc/1 ]). %% Functions meant to be used by external libraries like Spectral -ignore_xref([get_meta/1, set_meta/2, add_doc_to_type/2, normalize_doc/1, normalize_function_doc/1]). -spec can_be_missing( TypeInfo :: spectra:type_info(), Type :: spectra:sp_type() ) -> {true, spectra:missing_value()} | false. can_be_missing(TypeInfo, Type) -> case Type of #sp_type_with_variables{type = Type2} -> can_be_missing(TypeInfo, Type2); #sp_union{types = Types} -> case lists:filtermap(fun(T) -> can_be_missing(TypeInfo, T) end, Types) of [] -> false; MissingValues -> {true, lists:last(MissingValues)} end; #sp_literal{value = LiteralValue} when LiteralValue =:= nil orelse LiteralValue =:= undefined -> {true, LiteralValue}; #sp_user_type_ref{type_name = TypeName, variables = TypeArgs} -> TypeArity = length(TypeArgs), RefType = spectra_type_info:get_type(TypeInfo, TypeName, TypeArity), can_be_missing(TypeInfo, RefType); _ -> false end. -spec type_reference(spectra:sp_type_or_ref()) -> {true, spectra:sp_type_reference()} | false. type_reference({type, _, _} = Type) -> {true, Type}; type_reference({record, _} = Type) -> {true, Type}; type_reference(_) -> false. -spec get_meta(spectra:sp_type()) -> spectra:sp_type_meta(). get_meta(#sp_simple_type{meta = Meta}) -> Meta; get_meta(#sp_tuple{meta = Meta}) -> Meta; get_meta(#sp_map{meta = Meta}) -> Meta; get_meta(#sp_rec{meta = Meta}) -> Meta; get_meta(#sp_type_with_variables{meta = Meta}) -> Meta; get_meta(#sp_function{meta = Meta}) -> Meta; get_meta(#sp_union{meta = Meta}) -> Meta; get_meta(#sp_literal{meta = Meta}) -> Meta; get_meta(#sp_rec_ref{meta = Meta}) -> Meta; get_meta(#sp_remote_type{meta = Meta}) -> Meta; get_meta(#sp_maybe_improper_list{meta = Meta}) -> Meta; get_meta(#sp_nonempty_improper_list{meta = Meta}) -> Meta; get_meta(#sp_user_type_ref{meta = Meta}) -> Meta; get_meta(#sp_var{meta = Meta}) -> Meta; get_meta(#sp_range{meta = Meta}) -> Meta; get_meta(#sp_list{meta = Meta}) -> Meta; get_meta(#sp_nonempty_list{meta = Meta}) -> Meta. -spec set_meta(spectra:sp_type(), spectra:sp_type_meta()) -> spectra:sp_type(). set_meta(#sp_simple_type{} = T, Meta) -> T#sp_simple_type{meta = Meta}; set_meta(#sp_tuple{} = T, Meta) -> T#sp_tuple{meta = Meta}; set_meta(#sp_map{} = T, Meta) -> T#sp_map{meta = Meta}; set_meta(#sp_rec{} = T, Meta) -> T#sp_rec{meta = Meta}; set_meta(#sp_type_with_variables{} = T, Meta) -> T#sp_type_with_variables{meta = Meta}; set_meta(#sp_function{} = T, Meta) -> T#sp_function{meta = Meta}; set_meta(#sp_union{} = T, Meta) -> T#sp_union{meta = Meta}; set_meta(#sp_literal{} = T, Meta) -> T#sp_literal{meta = Meta}; set_meta(#sp_rec_ref{} = T, Meta) -> T#sp_rec_ref{meta = Meta}; set_meta(#sp_remote_type{} = T, Meta) -> T#sp_remote_type{meta = Meta}; set_meta(#sp_maybe_improper_list{} = T, Meta) -> T#sp_maybe_improper_list{meta = Meta}; set_meta(#sp_nonempty_improper_list{} = T, Meta) -> T#sp_nonempty_improper_list{meta = Meta}; set_meta(#sp_user_type_ref{} = T, Meta) -> T#sp_user_type_ref{meta = Meta}; set_meta(#sp_var{} = T, Meta) -> T#sp_var{meta = Meta}; set_meta(#sp_range{} = T, Meta) -> T#sp_range{meta = Meta}; set_meta(#sp_list{} = T, Meta) -> T#sp_list{meta = Meta}; set_meta(#sp_nonempty_list{} = T, Meta) -> T#sp_nonempty_list{meta = Meta}. -spec add_doc_to_type(spectra:sp_type(), map()) -> spectra:sp_type(). add_doc_to_type(Type, DocMap) -> Doc = normalize_doc(DocMap), Meta = get_meta(Type), set_meta(Type, Meta#{doc => Doc}). -spec normalize_doc(map()) -> spectra:type_doc(). normalize_doc(DocMap) -> maps:fold(fun add_doc_field/3, #{}, DocMap). -spec add_doc_field(atom(), term(), spectra:type_doc()) -> spectra:type_doc(). add_doc_field(title, Value, Acc) when is_binary(Value) -> Acc#{title => Value}; add_doc_field(description, Value, Acc) when is_binary(Value) -> Acc#{description => Value}; add_doc_field(examples, Value, Acc) when is_list(Value) -> Acc#{examples => Value}; add_doc_field(examples_function, {Module, Function, Args} = MFA, Acc) when is_atom(Module), is_atom(Function), is_list(Args) -> Acc#{examples_function => MFA}; add_doc_field(Key, Value, _Acc) -> erlang:error({invalid_spectra_field, Key, Value}). -spec normalize_function_doc(map()) -> spectra:function_doc(). normalize_function_doc(DocMap) -> maps:fold(fun add_function_doc_field/3, #{}, DocMap). -spec add_function_doc_field(atom(), term(), spectra:function_doc()) -> spectra:function_doc(). add_function_doc_field(summary, Value, Acc) when is_binary(Value) -> Acc#{summary => Value}; add_function_doc_field(description, Value, Acc) when is_binary(Value) -> Acc#{description => Value}; add_function_doc_field(deprecated, Value, Acc) when is_boolean(Value) -> Acc#{deprecated => Value}; add_function_doc_field(Key, Value, _Acc) -> erlang:error({invalid_spectra_field, Key, Value}).