Current section
Files
Jump to
Current section
Files
src/jokeapi@api.erl
-module(jokeapi@api).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([api_decoder/1, display_joke/1, get_any/0, get_joke_from_category/1]).
-export_type([joke_response/0, flags/0, api_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type joke_response() :: {joke_response,
integer(),
binary(),
flags(),
boolean(),
binary(),
binary(),
binary(),
binary(),
boolean()}.
-type flags() :: {flags,
boolean(),
boolean(),
boolean(),
boolean(),
boolean(),
boolean()}.
-type api_error() :: {decode_error, binary()} |
{generic_error, binary()} |
{request_error, binary()}.
-file("src/jokeapi/api.gleam", 55).
?DOC(
" Decodes the flags associated with a joke from the JokeAPI.\n"
" This function creates a decoder for the Flags type, which includes fields\n"
" for various sensitivities such as NSFW, religious, political, racist,\n"
" sexist, and explicit content. Each field is decoded as a boolean value.\n"
).
-spec flags_decoder() -> gleam@dynamic@decode:decoder(flags()).
flags_decoder() ->
gleam@dynamic@decode:field(
<<"nsfw"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_bool/1},
fun(Nsfw) ->
gleam@dynamic@decode:field(
<<"religious"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_bool/1},
fun(Religious) ->
gleam@dynamic@decode:field(
<<"political"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_bool/1},
fun(Political) ->
gleam@dynamic@decode:field(
<<"racist"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_bool/1},
fun(Racist) ->
gleam@dynamic@decode:field(
<<"sexist"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_bool/1},
fun(Sexist) ->
gleam@dynamic@decode:field(
<<"explicit"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_bool/1},
fun(Explicit) ->
gleam@dynamic@decode:success(
{flags,
Nsfw,
Religious,
Political,
Racist,
Sexist,
Explicit}
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/jokeapi/api.gleam", 83).
?DOC(
" Decodes a JSON string into a JokeResponse, handling potential decoding errors.\n"
"\n"
" This function attempts to parse a JSON string and convert it into a JokeResponse\n"
" using a predefined decoder. It maps any JSON decoding errors to APIError variants.\n"
"\n"
" # Arguments\n"
" - in: A JSON string representing a joke response\n"
"\n"
" # Returns\n"
" A Result containing either a successfully decoded JokeResponse or an APIError\n"
" if decoding fails\n"
).
-spec api_decoder(binary()) -> {ok, joke_response()} | {error, api_error()}.
api_decoder(In) ->
To_apierror = fun(In@1) -> case In@1 of
unexpected_end_of_input ->
{decode_error, <<"Unexpected end of input"/utf8>>};
{unexpected_byte, B} ->
{decode_error, <<"Unexpected byte: "/utf8, B/binary>>};
{unexpected_sequence, S} ->
{decode_error, <<"Unexpected sequence: "/utf8, S/binary>>};
{unable_to_decode, _} ->
{decode_error, <<"Unable to decode"/utf8>>}
end end,
Main_decoder = begin
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Id) ->
gleam@dynamic@decode:field(
<<"category"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Category) ->
gleam@dynamic@decode:field(
<<"flags"/utf8>>,
flags_decoder(),
fun(Flags) ->
gleam@dynamic@decode:field(
<<"safe"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_bool/1},
fun(Safe) ->
gleam@dynamic@decode:field(
<<"lang"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Lang) ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Type_) ->
gleam@dynamic@decode:field(
<<"setup"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Setup) ->
gleam@dynamic@decode:field(
<<"delivery"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(
Delivery
) ->
gleam@dynamic@decode:field(
<<"error"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_bool/1},
fun(
Error
) ->
gleam@dynamic@decode:success(
{joke_response,
Id,
Category,
Flags,
Safe,
Lang,
Type_,
Setup,
Delivery,
Error}
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end,
_pipe = gleam@json:parse(In, Main_decoder),
gleam@result:map_error(_pipe, To_apierror).
-file("src/jokeapi/api.gleam", 122).
?DOC(
" Converts a Hackney error into an APIError.\n"
" This function maps specific Hackney error types to corresponding APIError variants,\n"
" allowing for more meaningful error handling in the context of the JokeAPI.\n"
).
-spec hackney_error_to_apierror(gleam@hackney:error()) -> api_error().
hackney_error_to_apierror(In) ->
case In of
invalid_utf8_response ->
{request_error, <<"Received incompatible data"/utf8>>};
{other, _} ->
{request_error, <<"Generic error from Hackney"/utf8>>}
end.
-file("src/jokeapi/api.gleam", 133).
?DOC(
" Retrieves a joke from the JokeAPI using the provided URI.\n"
" This function constructs an HTTP request to the JokeAPI,\n"
" sends the request, and decodes the response into a JokeResponse.\n"
" If any step fails, it returns an APIError.\n"
).
-spec fetch_joke(gleam@uri:uri()) -> {ok, joke_response()} |
{error, api_error()}.
fetch_joke(In) ->
gleam@result:'try'(
begin
_pipe = gleam@http@request:from_uri(In),
_pipe@1 = gleam@result:map(
_pipe,
fun(_capture) ->
gleam@http@request:set_header(
_capture,
<<"User-Agent"/utf8>>,
<<"Joker"/utf8>>
)
end
),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{request_error, <<"Could not create request"/utf8>>}
end
)
end,
fun(Request) ->
gleam@result:'try'(
begin
_pipe@2 = gleam@hackney:send(Request),
gleam@result:map_error(
_pipe@2,
fun hackney_error_to_apierror/1
)
end,
fun(_use0) ->
{response, _, _, Data} = _use0,
api_decoder(Data)
end
)
end
).
-file("src/jokeapi/api.gleam", 211).
?DOC(" Adds a flag to the list of flags if the corresponding condition is active.\n").
-spec add_flag_if_active(list(binary()), binary(), boolean()) -> list(binary()).
add_flag_if_active(Flags, Flag_name, Is_active) ->
case Is_active of
true ->
[Flag_name | Flags];
false ->
Flags
end.
-file("src/jokeapi/api.gleam", 200).
?DOC(" Retrieves the active flags from a Flags type.\n").
-spec get_active_flags(flags()) -> list(binary()).
get_active_flags(Flags) ->
_pipe = [],
_pipe@1 = add_flag_if_active(
_pipe,
<<"NSFW"/utf8>>,
erlang:element(2, Flags)
),
_pipe@2 = add_flag_if_active(
_pipe@1,
<<"Religious"/utf8>>,
erlang:element(3, Flags)
),
_pipe@3 = add_flag_if_active(
_pipe@2,
<<"Political"/utf8>>,
erlang:element(4, Flags)
),
_pipe@4 = add_flag_if_active(
_pipe@3,
<<"Racist"/utf8>>,
erlang:element(5, Flags)
),
_pipe@5 = add_flag_if_active(
_pipe@4,
<<"Sexist"/utf8>>,
erlang:element(6, Flags)
),
add_flag_if_active(_pipe@5, <<"Explicit"/utf8>>, erlang:element(7, Flags)).
-file("src/jokeapi/api.gleam", 186).
?DOC(" Displays metadata about the joke, including language and safety status.\n").
-spec display_metadata(flags(), binary(), boolean()) -> nil.
display_metadata(Flags, Lang, Safe) ->
Safe_status = case Safe of
true ->
<<"✅ Safe"/utf8>>;
false ->
<<"⚠️ Not Safe"/utf8>>
end,
gleam_stdlib:println(
<<<<<<"🏷️ Language: "/utf8, (string:uppercase(Lang))/binary>>/binary,
" | "/utf8>>/binary,
Safe_status/binary>>
),
Active_flags = get_active_flags(Flags),
case gleam@list:is_empty(Active_flags) of
false ->
gleam_stdlib:println(
<<"🚩 Flags: "/utf8,
(gleam@string:join(Active_flags, <<", "/utf8>>))/binary>>
);
true ->
nil
end.
-file("src/jokeapi/api.gleam", 173).
?DOC(" Displays a joke in a formatted manner.\n").
-spec display_joke(joke_response()) -> nil.
display_joke(Joke) ->
gleam_stdlib:println(
<<<<<<"🎯 "/utf8, (string:uppercase(erlang:element(3, Joke)))/binary>>/binary,
" JOKE #"/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(2, Joke)))/binary>>
),
gleam_stdlib:println(<<"❓ "/utf8, (erlang:element(8, Joke))/binary>>),
gleam_stdlib:println(<<"💡 "/utf8, (erlang:element(9, Joke))/binary>>),
display_metadata(
erlang:element(4, Joke),
erlang:element(6, Joke),
erlang:element(5, Joke)
).
-file("src/jokeapi/api.gleam", 151).
?DOC(
" Fetches a random joke from the JokeAPI.\n"
" This function constructs a URI for the \"Any\" endpoint of the JokeAPI,\n"
" sends a request to that endpoint, and returns a JokeResponse.\n"
).
-spec get_any() -> {ok, joke_response()} | {error, api_error()}.
get_any() ->
gleam@result:'try'(
begin
_pipe = gleam_stdlib:uri_parse(
<<"https://v2.jokeapi.dev/joke/"/utf8, "Any"/utf8>>
),
gleam@result:map_error(
_pipe,
fun(_) -> {request_error, <<"Could not build URI"/utf8>>} end
)
end,
fun(Uri) -> fetch_joke(Uri) end
).
-file("src/jokeapi/api.gleam", 162).
?DOC(
" Fetches a joke in a specific category from the JokeAPI.\n"
" This function constructs a URI for the \"Programming\" endpoint of the JokeAPI,\n"
" sends a request to that endpoint, and returns a JokeResponse.\n"
).
-spec get_joke_from_category(binary()) -> {ok, joke_response()} |
{error, api_error()}.
get_joke_from_category(Category) ->
gleam@result:'try'(
begin
_pipe = gleam_stdlib:uri_parse(
<<"https://v2.jokeapi.dev/joke/"/utf8, Category/binary>>
),
gleam@result:map_error(
_pipe,
fun(_) -> {request_error, <<"Could not build URI"/utf8>>} end
)
end,
fun(Uri) -> fetch_joke(Uri) end
).