Skip to content

Commit

Permalink
Various fixes for 1.1.4 Argo spec compatibility.
Browse files Browse the repository at this point in the history
  • Loading branch information
potatosalad committed Apr 17, 2024
1 parent 63440f7 commit d8b8431
Show file tree
Hide file tree
Showing 42 changed files with 834 additions and 390 deletions.
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,17 @@ distclean-erlfmt:

format: $(ERLFMT)
$(verbose) $(MAKE) erlfmt

.PHONY: lint lint-dialyzer lint-eqwalizer lint-format lint-xref

lint:: lint-format lint-eqwalizer lint-xref lint-dialyzer

lint-dialyzer:
$(verbose) rebar3 dialyzer

lint-eqwalizer: eqwalizer

lint-format: erlfmt-check

lint-xref:
$(verbose) rebar3 xref
2 changes: 2 additions & 0 deletions apps/argo/include/argo_label.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
-define(ARGO_LABEL_MARKER_ERROR, -3).
% ?ARGO_LABEL_MARKER_ERROR
-define(ARGO_LABEL_MARKER_LOWEST_RESERVED_VALUE, -3).
% ?ARGO_LABEL_MARKER_LOWEST_RESERVED_VALUE - 1
-define(ARGO_LABEL_MARKER_OFFSET_FACTOR, -4).

% ?ARGO_LABEL_MARKER_NULL
-define(ARGO_LABEL_SELF_DESCRIBING_MARKER_NULL, -1).
Expand Down
2 changes: 1 addition & 1 deletion apps/argo/include/argo_value.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

-record(argo_error_value, {
message :: argo_desc_value:desc_string(),
location :: none | {some, [argo_location_value:t()]},
locations :: none | {some, [argo_location_value:t()]},
path :: none | {some, argo_path_value:t()},
extensions :: none | {some, argo_extensions_value:t()}
}).
Expand Down
9 changes: 7 additions & 2 deletions apps/argo/include/argo_wire_type.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

-record(argo_field_wire_type, {
name :: argo_types:name(),
type :: argo_wire_type:t(),
'of' :: argo_wire_type:t(),
omittable :: boolean()
}).

Expand Down Expand Up @@ -80,7 +80,12 @@
}).

-record(argo_wire_type_store, {
types :: argo_index_map:t(argo_types:name(), argo_wire_type:t())
types :: argo_index_map:t(argo_types:name(), argo_wire_type_store_entry:t())
}).

-record(argo_wire_type_store_entry, {
name :: argo_types:name(),
type :: argo_wire_type:t()
}).

-endif.
117 changes: 92 additions & 25 deletions apps/argo/src/argo.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,71 +21,138 @@
-export([
display/1,
display/2,
display/3,
display_with_lines/1,
display_with_lines/2,
display_with_lines/3,
format/1,
format_with_lines/1
format/2,
format_with_lines/1,
format_with_lines/2
]).
%% Errors API
-export([
format_error/2
]).

%% Macros
-define(DEFAULT_IO_DEVICE, standard_io).
-define(DEFAULT_OPTIONS, #{}).
-define(is_record(X), (is_tuple((X)) andalso tuple_size((X)) > 0 andalso is_atom(element(1, (X))))).

%%%=============================================================================
%%% API functions
%%%=============================================================================

-spec display(dynamic()) -> ok.
-spec display(Type) -> ok when Type :: dynamic().
display(Type) when ?is_record(Type) ->
display(standard_io, Type).
display(?DEFAULT_IO_DEVICE, Type, ?DEFAULT_OPTIONS).

-spec display(io:device(), dynamic()) -> ok.
-spec display(IoDevice | Type, Type | Options) -> ok when
IoDevice :: io:device(), Type :: dynamic(), Options :: dynamic().
display(IoDevice, Type) when not is_list(IoDevice) andalso ?is_record(Type) ->
Module = element(1, Type),
case Module of
display(IoDevice, Type, ?DEFAULT_OPTIONS);
display(Type, Options) when ?is_record(Type) andalso is_map(Options) ->
display(?DEFAULT_IO_DEVICE, Type, Options).

-spec display(IoDevice, Type, Options) -> ok when IoDevice :: io:device(), Type :: dynamic(), Options :: dynamic().
display(IoDevice, Type, Options) when not is_list(IoDevice) andalso ?is_record(Type) andalso is_map(Options) ->
case element(1, Type) of
argo_value ->
argo_value:display(IoDevice, Type);
argo_value:display(IoDevice, Type, Options);
argo_wire_type ->
argo_wire_type:display(IoDevice, Type);
argo_wire_type:display(IoDevice, Type, Options);
argo_wire_type_store ->
argo_wire_type_store:display(IoDevice, Type);
_ ->
argo_wire_type_store:display(IoDevice, Type, Options);
Module ->
case erlang:atom_to_binary(Module, utf8) of
<<"argo_graphql_", _/bytes>> ->
argo_graphql:display(IoDevice, Type)
argo_graphql:display(IoDevice, Type, Options);
_ ->
error_with_info(badarg, [IoDevice, Type, Options], #{2 => display_not_supported})
end
end.

-spec display_with_lines(dynamic()) -> ok.
-spec display_with_lines(Type) -> ok when Type :: dynamic().
display_with_lines(Type) when ?is_record(Type) ->
display_with_lines(standard_io, Type).
display_with_lines(?DEFAULT_IO_DEVICE, Type, ?DEFAULT_OPTIONS).

-spec display_with_lines(io:device(), dynamic()) -> ok.
-spec display_with_lines(IoDevice | Type, Type | Options) -> ok when
IoDevice :: io:device(), Type :: dynamic(), Options :: dynamic().
display_with_lines(IoDevice, Type) when not is_list(IoDevice) andalso ?is_record(Type) ->
Lines = format_with_lines(Type),
Printer1 = argo_graphql_printer:new_io_device(IoDevice),
display_with_lines(IoDevice, Type, ?DEFAULT_OPTIONS);
display_with_lines(Type, Options) when ?is_record(Type) andalso is_map(Options) ->
display_with_lines(?DEFAULT_IO_DEVICE, Type, Options).

-spec display_with_lines(IoDevice, Type, Options) -> ok when
IoDevice :: io:device(), Type :: dynamic(), Options :: dynamic().
display_with_lines(IoDevice, Type, Options) when
not is_list(IoDevice) andalso ?is_record(Type) andalso is_map(Options)
->
Lines = format_with_lines(Type, Options),
Printer1 = argo_graphql_printer:new_io_device(IoDevice, Options),
Printer2 = argo_graphql_printer:write(Printer1, "~ts", [Lines]),
case argo_graphql_printer:finalize(Printer2) of
ok ->
ok
end.

-spec format(dynamic()) -> unicode:unicode_binary().
-spec format(Type) -> Output when Type :: dynamic(), Output :: unicode:unicode_binary().
format(Type) when ?is_record(Type) ->
Module = element(1, Type),
case Module of
format(Type, ?DEFAULT_OPTIONS).

-spec format(Type, Options) -> Output when Type :: dynamic(), Options :: dynamic(), Output :: unicode:unicode_binary().
format(Type, Options) when ?is_record(Type) andalso is_map(Options) ->
case element(1, Type) of
argo_value ->
argo_types:unicode_binary(argo_value:format(Type));
argo_value:format(Type, Options);
argo_wire_type ->
argo_types:unicode_binary(argo_wire_type:format(Type));
argo_wire_type:format(Type, Options);
argo_wire_type_store ->
argo_types:unicode_binary(argo_wire_type_store:format(Type));
_ ->
argo_wire_type_store:format(Type, Options);
Module ->
case erlang:atom_to_binary(Module, utf8) of
<<"argo_graphql_", _/bytes>> ->
argo_graphql:format(Type)
argo_graphql:format(Type, Options);
_ ->
error_with_info(badarg, [Type, Options], #{1 => format_not_supported})
end
end.

-spec format_with_lines(dynamic()) -> unicode:unicode_binary().
format_with_lines(Type) when ?is_record(Type) ->
argo_types:format_with_lines(format(Type)).
format_with_lines(Type, ?DEFAULT_OPTIONS).

-spec format_with_lines(Type, Options) -> Output when
Type :: dynamic(), Options :: dynamic(), Output :: unicode:unicode_binary().
format_with_lines(Type, Options) when ?is_record(Type) andalso is_map(Options) ->
argo_types:format_with_lines(format(Type, Options)).

%%%=============================================================================
%%% Errors API functions
%%%=============================================================================

%% @private
-spec error_with_info(dynamic(), dynamic(), dynamic()) -> no_return().
error_with_info(Reason, Args, Cause) ->
erlang:error(Reason, Args, [{error_info, #{module => ?MODULE, cause => Cause}}]).

-spec format_error(dynamic(), dynamic()) -> dynamic().
format_error(_Reason, [{_M, _F, _As, Info} | _]) ->
ErrorInfo = proplists:get_value(error_info, Info, #{}),
ErrorDescription1 = maps:get(cause, ErrorInfo),
ErrorDescription2 = maps:map(fun format_error_description/2, ErrorDescription1),
ErrorDescription2.

%% @private
-spec format_error_description(dynamic(), dynamic()) -> dynamic().
format_error_description(_Key, display_not_supported) ->
"display/3 is not supported for this Type";
format_error_description(_Key, format_not_supported) ->
"format/2 is not supported for this Type";
format_error_description(_Key, Value) ->
Value.

%%%-----------------------------------------------------------------------------
%%% Internal functions
%%%-----------------------------------------------------------------------------
41 changes: 41 additions & 0 deletions apps/argo/src/argo_debug_type.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
%%%-----------------------------------------------------------------------------
%%% Copyright (c) Meta Platforms, Inc. and affiliates.
%%% Copyright (c) WhatsApp LLC
%%%
%%% This source code is licensed under the MIT license found in the
%%% LICENSE.md file in the root directory of this source tree.
%%%
%%% @author Andrew Bennett <potatosaladx@meta.com>
%%% @copyright (c) Meta Platforms, Inc. and affiliates.
%%% @doc
%%%
%%% @end
%%% Created : 17 Apr 2024 by Andrew Bennett <potatosaladx@meta.com>
%%%-----------------------------------------------------------------------------
%%% % @format
-module(argo_debug_type).
-compile(warn_missing_spec_all).
-oncall("whatsapp_clr").

%% Behaviour
-callback display(IoDevice, Type, Options) -> ok when
IoDevice :: io:device(), Type :: type(), Options :: options().
-callback format(Type) -> Output when
Type :: type(), Output :: output().
-callback format(Type, Options) -> Output when
Type :: type(), Options :: options(), Output :: output().

-optional_callbacks([
format/1
]).

%% Types
-type options() :: dynamic().
-type output() :: unicode:unicode_binary().
-type type() :: dynamic().

-export_type([
options/0,
output/0,
type/0
]).
7 changes: 1 addition & 6 deletions apps/argo/src/argo_header.erl
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@
-compile(warn_missing_spec_all).
-oncall("whatsapp_clr").

-compile(
{inline, [
error_with_info/3
]}
).

-include_lib("argo/include/argo_header.hrl").

%% Codec API
Expand Down Expand Up @@ -302,6 +296,7 @@ set_user_flags(H0 = #argo_header{}, New) when is_bitstring(New) ->
%%%=============================================================================

%% @private
-compile({inline, [error_with_info/3]}).
-spec error_with_info(dynamic(), dynamic(), dynamic()) -> no_return().
error_with_info(Reason, Args, Cause) ->
erlang:error(Reason, Args, [{error_info, #{module => ?MODULE, cause => Cause}}]).
Expand Down
8 changes: 4 additions & 4 deletions apps/argo/src/argo_typer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ path_to_wire_path(WireType = #argo_wire_type{inner = RecordWireType = #argo_reco
is_binary(FieldName) andalso is_list(Path)
->
case argo_record_wire_type:find_index_of(RecordWireType, FieldName) of
{ok, FieldIndex, _FieldWireType = #argo_field_wire_type{name = FieldName, type = Type}} ->
[FieldIndex | path_to_wire_path(Type, Path)];
{ok, FieldIndex, _FieldWireType = #argo_field_wire_type{name = FieldName, 'of' = FieldOf}} ->
[FieldIndex | path_to_wire_path(FieldOf, Path)];
error ->
error_with_info(badarg, [WireType, [FieldName | Path]], #{2 => {missing_field_name, FieldName}})
end;
Expand Down Expand Up @@ -199,8 +199,8 @@ wire_path_to_path(WireType = #argo_wire_type{inner = RecordWireType = #argo_reco
?is_usize(FieldIndex) andalso is_list(WirePath)
->
case argo_record_wire_type:find_index(RecordWireType, FieldIndex) of
{ok, _FieldWireType = #argo_field_wire_type{name = FieldName, type = Type}} ->
[FieldName | wire_path_to_path(Type, WirePath)];
{ok, _FieldWireType = #argo_field_wire_type{name = FieldName, 'of' = FieldOf}} ->
[FieldName | wire_path_to_path(FieldOf, WirePath)];
error ->
error_with_info(badarg, [WireType, [FieldIndex | WirePath]], #{2 => {missing_field_index, FieldIndex}})
end;
Expand Down
4 changes: 2 additions & 2 deletions apps/argo/src/core/argo_core_reader.erl
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ read_label(R = #argo_core_reader{}) ->
CoreReader :: t(), LabeledType :: argo_core:labeled_type().
read_labeled_type(R0 = #argo_core_reader{}) ->
{R1, LengthOrBackreference} = read_varint(R0),
case LengthOrBackreference =< ?ARGO_LABEL_MARKER_LOWEST_RESERVED_VALUE of
case LengthOrBackreference < ?ARGO_LABEL_MARKER_LOWEST_RESERVED_VALUE of
true ->
Backreference = abs(LengthOrBackreference - ?ARGO_LABEL_MARKER_LOWEST_RESERVED_VALUE),
Backreference = -LengthOrBackreference + ?ARGO_LABEL_MARKER_OFFSET_FACTOR,
ok = backreference_sanity_check(Backreference),
{R1, {backreference, Backreference}};
false ->
Expand Down
2 changes: 1 addition & 1 deletion apps/argo/src/core/argo_core_writer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ write_label(W = #argo_core_writer{}, Label) ->
CoreWriter :: t(), LabeledType :: argo_core:labeled_type().
write_labeled_type(W = #argo_core_writer{}, {backreference, Backreference}) when ?is_usize(Backreference) ->
ok = backreference_sanity_check(Backreference),
Label = ?ARGO_LABEL_MARKER_LOWEST_RESERVED_VALUE - Backreference,
Label = ?ARGO_LABEL_MARKER_OFFSET_FACTOR - Backreference,
write_label(W, Label);
write_labeled_type(W = #argo_core_writer{}, {length, Length}) when ?is_usize(Length) ->
write_length(W, Length).
Expand Down
Loading

0 comments on commit d8b8431

Please sign in to comment.