Skip to main content

src/oaisp@schema.erl

-module(oaisp@schema).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaisp/schema.gleam").
-export([type_ref/2, type_ref_parts/1, schema_to_json/1, schema_decoder/0]).
-export_type([schema/0, scalar_kind/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.

?MODULEDOC(
    " A schema reference for an endpoint's body, response, or parameter.\n"
    "\n"
    " oaisp is non-intrusive: it never sees your decoders or encoders. You point\n"
    " an endpoint at a type by name with [`type_ref`](#type_ref) — resolved from\n"
    " the package interface at build time — or at an inline scalar via\n"
    " `oaisp/param`. How your handlers read and write that type is entirely yours.\n"
).

-type schema() :: {type_ref, binary(), binary()} |
    {scalar, scalar_kind(), gleam@option:option(binary())}.

-type scalar_kind() :: string_kind | int_kind | bool_kind | float_kind.

-file("src/oaisp/schema.gleam", 33).
?DOC(
    " A [`Schema`](#Schema) referring to a public Gleam type, resolved at merge\n"
    " time from the package interface.\n"
).
-spec type_ref(binary(), binary()) -> schema().
type_ref(Module, Name) ->
    {type_ref, Module, Name}.

-file("src/oaisp/schema.gleam", 40).
?DOC(false).
-spec type_ref_parts(schema()) -> {ok, {binary(), binary()}} | {error, nil}.
type_ref_parts(Schema) ->
    case Schema of
        {type_ref, Module, Name} ->
            {ok, {Module, Name}};

        {scalar, _, _} ->
            {error, nil}
    end.

-file("src/oaisp/schema.gleam", 92).
-spec scalar_kind_to_string(scalar_kind()) -> binary().
scalar_kind_to_string(Kind) ->
    case Kind of
        string_kind ->
            <<"string"/utf8>>;

        int_kind ->
            <<"int"/utf8>>;

        bool_kind ->
            <<"bool"/utf8>>;

        float_kind ->
            <<"float"/utf8>>
    end.

-file("src/oaisp/schema.gleam", 50).
?DOC(false).
-spec schema_to_json(schema()) -> gleam@json:json().
schema_to_json(Schema) ->
    case Schema of
        {type_ref, Module, Name} ->
            gleam@json:object(
                [{<<"kind"/utf8>>, gleam@json:string(<<"type_ref"/utf8>>)},
                    {<<"module"/utf8>>, gleam@json:string(Module)},
                    {<<"name"/utf8>>, gleam@json:string(Name)}]
            );

        {scalar, Kind, Description} ->
            gleam@json:object(
                [{<<"kind"/utf8>>, gleam@json:string(<<"scalar"/utf8>>)},
                    {<<"scalar"/utf8>>,
                        gleam@json:string(scalar_kind_to_string(Kind))},
                    {<<"description"/utf8>>,
                        gleam@json:nullable(
                            Description,
                            fun gleam@json:string/1
                        )}]
            )
    end.

-file("src/oaisp/schema.gleam", 101).
-spec scalar_kind_from_string(binary()) -> {ok, scalar_kind()} | {error, nil}.
scalar_kind_from_string(Name) ->
    case Name of
        <<"string"/utf8>> ->
            {ok, string_kind};

        <<"int"/utf8>> ->
            {ok, int_kind};

        <<"bool"/utf8>> ->
            {ok, bool_kind};

        <<"float"/utf8>> ->
            {ok, float_kind};

        _ ->
            {error, nil}
    end.

-file("src/oaisp/schema.gleam", 69).
?DOC(false).
-spec schema_decoder() -> gleam@dynamic@decode:decoder(schema()).
schema_decoder() ->
    gleam@dynamic@decode:field(
        <<"kind"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Kind) -> case Kind of
                <<"type_ref"/utf8>> ->
                    gleam@dynamic@decode:field(
                        <<"module"/utf8>>,
                        {decoder, fun gleam@dynamic@decode:decode_string/1},
                        fun(Module) ->
                            gleam@dynamic@decode:field(
                                <<"name"/utf8>>,
                                {decoder,
                                    fun gleam@dynamic@decode:decode_string/1},
                                fun(Name) ->
                                    gleam@dynamic@decode:success(
                                        {type_ref, Module, Name}
                                    )
                                end
                            )
                        end
                    );

                <<"scalar"/utf8>> ->
                    gleam@dynamic@decode:field(
                        <<"scalar"/utf8>>,
                        {decoder, fun gleam@dynamic@decode:decode_string/1},
                        fun(Scalar) ->
                            gleam@dynamic@decode:field(
                                <<"description"/utf8>>,
                                gleam@dynamic@decode:optional(
                                    {decoder,
                                        fun gleam@dynamic@decode:decode_string/1}
                                ),
                                fun(Description) ->
                                    case scalar_kind_from_string(Scalar) of
                                        {ok, Kind@1} ->
                                            gleam@dynamic@decode:success(
                                                {scalar, Kind@1, Description}
                                            );

                                        {error, nil} ->
                                            gleam@dynamic@decode:failure(
                                                {type_ref,
                                                    <<""/utf8>>,
                                                    <<""/utf8>>},
                                                <<"ScalarKind"/utf8>>
                                            )
                                    end
                                end
                            )
                        end
                    );

                _ ->
                    gleam@dynamic@decode:failure(
                        {type_ref, <<""/utf8>>, <<""/utf8>>},
                        <<"Schema"/utf8>>
                    )
            end end
    ).