Packages
oaspec
0.65.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.1
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.3
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.1.3
Generate Gleam code from OpenAPI 3.x specifications
Current section
Files
Jump to
Current section
Files
src/oaspec@codegen@writer.erl
-module(oaspec@codegen@writer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/codegen/writer.gleam").
-export([write_all/3, resolve_paths/2, expected_paths/2, output_dirs/1, error_to_string/1]).
-export_type([write_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 write_error() :: {directory_create_error, binary(), binary()} |
{file_write_error, binary(), binary()}.
-file("src/oaspec/codegen/writer.gleam", 88).
-spec normalise_dir(binary()) -> binary().
normalise_dir(Path) ->
gleam@bool:guard(
not gleam_stdlib:string_ends_with(Path, <<"/"/utf8>>),
Path,
fun() -> gleam@string:drop_end(Path, 1) end
).
-file("src/oaspec/codegen/writer.gleam", 94).
?DOC(" Ensure a directory exists, creating it if necessary.\n").
-spec ensure_directory(binary()) -> {ok, nil} | {error, write_error()}.
ensure_directory(Path) ->
_pipe = simplifile:create_directory_all(Path),
gleam@result:map_error(
_pipe,
fun(Err) ->
{directory_create_error,
Path,
<<"Failed to create directory: "/utf8,
(simplifile:describe_error(Err))/binary>>}
end
).
-file("src/oaspec/codegen/writer.gleam", 110).
?DOC(
" Write a list of generated files to a directory.\n"
"\n"
" Issue #247: files marked `SkipIfExists` (currently `handlers.gleam`)\n"
" are written only on first generation. If the file already exists on\n"
" disk, it is left alone — the user owns the contents and the\n"
" generator must not clobber their implementation.\n"
).
-spec write_files(
list(oaspec@internal@codegen@context:generated_file()),
binary(),
list(binary()),
fun((binary()) -> nil)
) -> {ok, list(binary())} | {error, write_error()}.
write_files(Files, Base_path, Written, On_write) ->
gleam@list:try_fold(
Files,
Written,
fun(Acc, File) ->
Full_path = <<<<Base_path/binary, "/"/utf8>>/binary,
(erlang:element(2, File))/binary>>,
case {erlang:element(5, File), simplifile_erl:is_file(Full_path)} of
{skip_if_exists, {ok, true}} ->
{ok, Acc};
{_, _} ->
gleam@result:'try'(
begin
_pipe = simplifile:write(
Full_path,
erlang:element(3, File)
),
gleam@result:map_error(
_pipe,
fun(Err) ->
{file_write_error,
Full_path,
<<"Failed to write file: "/utf8,
(simplifile:describe_error(Err))/binary>>}
end
)
end,
fun(_) ->
On_write(Full_path),
{ok, [Full_path | Acc]}
end
)
end
end
).
-file("src/oaspec/codegen/writer.gleam", 25).
?DOC(
" Write pre-generated files to disk based on configuration.\n"
"\n"
" **Path overlap.** When `mode = Both` and `output_server` resolves\n"
" to the same directory as `output_client` (e.g. both set to\n"
" `\"out\"`, or to `\"out\"` and `\"out/\"`, which differ as strings but\n"
" name the same directory), shared files are written **once**, not\n"
" twice. Each unique destination path triggers `on_write` exactly\n"
" once and appears at most once in the returned list. Distinct\n"
" server / client paths preserve the existing behaviour: shared\n"
" files land in both directories.\n"
).
-spec write_all(
list(oaspec@internal@codegen@context:generated_file()),
oaspec@config:config(),
fun((binary()) -> nil)
) -> {ok, list(binary())} | {error, write_error()}.
write_all(Files, Cfg, On_write) ->
Shared_files = gleam@list:filter(
Files,
fun(F) -> erlang:element(4, F) =:= shared_target end
),
Server_files = gleam@list:filter(
Files,
fun(F@1) -> erlang:element(4, F@1) =:= server_target end
),
Client_files = gleam@list:filter(
Files,
fun(F@2) -> erlang:element(4, F@2) =:= client_target end
),
Server_path = oaspec@config:output_server(Cfg),
Client_path = oaspec@config:output_client(Cfg),
Mode = oaspec@config:mode(Cfg),
Server_and_client_overlap = (Mode =:= both) andalso (normalise_dir(
Server_path
)
=:= normalise_dir(Client_path)),
Written_files = [],
gleam@result:'try'(case Mode of
server ->
gleam@result:'try'(
ensure_directory(Server_path),
fun(_) ->
_pipe = write_files(
Shared_files,
Server_path,
Written_files,
On_write
),
gleam@result:'try'(
_pipe,
fun(W) ->
write_files(
Server_files,
Server_path,
W,
On_write
)
end
)
end
);
both ->
gleam@result:'try'(
ensure_directory(Server_path),
fun(_) ->
_pipe = write_files(
Shared_files,
Server_path,
Written_files,
On_write
),
gleam@result:'try'(
_pipe,
fun(W) ->
write_files(
Server_files,
Server_path,
W,
On_write
)
end
)
end
);
client ->
{ok, Written_files}
end, fun(Written_files@1) -> gleam@result:'try'(case Mode of
client ->
gleam@result:'try'(
ensure_directory(Client_path),
fun(_) ->
Shared_for_client = case Server_and_client_overlap of
true ->
[];
false ->
Shared_files
end,
_pipe@1 = write_files(
Shared_for_client,
Client_path,
Written_files@1,
On_write
),
gleam@result:'try'(
_pipe@1,
fun(W@1) ->
write_files(
Client_files,
Client_path,
W@1,
On_write
)
end
)
end
);
both ->
gleam@result:'try'(
ensure_directory(Client_path),
fun(_) ->
Shared_for_client = case Server_and_client_overlap of
true ->
[];
false ->
Shared_files
end,
_pipe@1 = write_files(
Shared_for_client,
Client_path,
Written_files@1,
On_write
),
gleam@result:'try'(
_pipe@1,
fun(W@1) ->
write_files(
Client_files,
Client_path,
W@1,
On_write
)
end
)
end
);
server ->
{ok, Written_files@1}
end, fun(Written_files@2) -> {ok, Written_files@2} end) end).
-file("src/oaspec/codegen/writer.gleam", 154).
?DOC(
" Resolve generated files to their full output paths and content.\n"
" Used by --check to compare against existing files without writing.\n"
"\n"
" Issue #247: `SkipIfExists` files (e.g. user-owned `handlers.gleam`)\n"
" are dropped from the result. `--check` is meant to flag drift in\n"
" generator-owned files; user-edited files are expected to differ\n"
" from the bootstrap stub and should not be reported as out-of-date.\n"
"\n"
" Issue #548: when `mode = Both` and `output_server` resolves to the\n"
" same directory as `output_client`, shared files appear once in\n"
" the result, not twice — `--check` would otherwise compare the\n"
" same file twice and double-count any drift.\n"
).
-spec resolve_paths(
list(oaspec@internal@codegen@context:generated_file()),
oaspec@config:config()
) -> list({binary(), binary()}).
resolve_paths(Files, Cfg) ->
Files@1 = gleam@list:filter(
Files,
fun(F) -> erlang:element(5, F) /= skip_if_exists end
),
Shared_files = gleam@list:filter(
Files@1,
fun(F@1) -> erlang:element(4, F@1) =:= shared_target end
),
Server_files = gleam@list:filter(
Files@1,
fun(F@2) -> erlang:element(4, F@2) =:= server_target end
),
Client_files = gleam@list:filter(
Files@1,
fun(F@3) -> erlang:element(4, F@3) =:= client_target end
),
Server_path = oaspec@config:output_server(Cfg),
Client_path = oaspec@config:output_client(Cfg),
Mode = oaspec@config:mode(Cfg),
Server_and_client_overlap = (Mode =:= both) andalso (normalise_dir(
Server_path
)
=:= normalise_dir(Client_path)),
Server_entries = case Mode of
server ->
gleam@list:map(
lists:append(Shared_files, Server_files),
fun(F@4) ->
{<<<<Server_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@4))/binary>>,
erlang:element(3, F@4)}
end
);
both ->
gleam@list:map(
lists:append(Shared_files, Server_files),
fun(F@4) ->
{<<<<Server_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@4))/binary>>,
erlang:element(3, F@4)}
end
);
client ->
[]
end,
Shared_for_client = case Server_and_client_overlap of
true ->
[];
false ->
Shared_files
end,
Client_entries = case Mode of
client ->
gleam@list:map(
lists:append(Shared_for_client, Client_files),
fun(F@5) ->
{<<<<Client_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@5))/binary>>,
erlang:element(3, F@5)}
end
);
both ->
gleam@list:map(
lists:append(Shared_for_client, Client_files),
fun(F@5) ->
{<<<<Client_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@5))/binary>>,
erlang:element(3, F@5)}
end
);
server ->
[]
end,
lists:append(Server_entries, Client_entries).
-file("src/oaspec/codegen/writer.gleam", 202).
?DOC(
" Every file path the generator would write for the given config — including\n"
" `SkipIfExists` files. Used by `--check` to keep user-owned `handlers.gleam`\n"
" off the orphan list while still excluding it from byte-comparison drift\n"
" reports (`resolve_paths` drops `SkipIfExists` entries).\n"
"\n"
" Same path-overlap dedup as `resolve_paths` (#548): shared files\n"
" appear once when the two output directories coincide.\n"
).
-spec expected_paths(
list(oaspec@internal@codegen@context:generated_file()),
oaspec@config:config()
) -> list(binary()).
expected_paths(Files, Cfg) ->
Shared_files = gleam@list:filter(
Files,
fun(F) -> erlang:element(4, F) =:= shared_target end
),
Server_files = gleam@list:filter(
Files,
fun(F@1) -> erlang:element(4, F@1) =:= server_target end
),
Client_files = gleam@list:filter(
Files,
fun(F@2) -> erlang:element(4, F@2) =:= client_target end
),
Server_path = oaspec@config:output_server(Cfg),
Client_path = oaspec@config:output_client(Cfg),
Mode = oaspec@config:mode(Cfg),
Server_and_client_overlap = (Mode =:= both) andalso (normalise_dir(
Server_path
)
=:= normalise_dir(Client_path)),
Server_entries = case Mode of
server ->
gleam@list:map(
lists:append(Shared_files, Server_files),
fun(F@3) ->
<<<<Server_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@3))/binary>>
end
);
both ->
gleam@list:map(
lists:append(Shared_files, Server_files),
fun(F@3) ->
<<<<Server_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@3))/binary>>
end
);
client ->
[]
end,
Shared_for_client = case Server_and_client_overlap of
true ->
[];
false ->
Shared_files
end,
Client_entries = case Mode of
client ->
gleam@list:map(
lists:append(Shared_for_client, Client_files),
fun(F@4) ->
<<<<Client_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@4))/binary>>
end
);
both ->
gleam@list:map(
lists:append(Shared_for_client, Client_files),
fun(F@4) ->
<<<<Client_path/binary, "/"/utf8>>/binary,
(erlang:element(2, F@4))/binary>>
end
);
server ->
[]
end,
lists:append(Server_entries, Client_entries).
-file("src/oaspec/codegen/writer.gleam", 243).
?DOC(" Return the output directories that would be written to for the given config.\n").
-spec output_dirs(oaspec@config:config()) -> list(binary()).
output_dirs(Cfg) ->
case oaspec@config:mode(Cfg) of
server ->
[oaspec@config:output_server(Cfg)];
client ->
[oaspec@config:output_client(Cfg)];
both ->
[oaspec@config:output_server(Cfg), oaspec@config:output_client(Cfg)]
end.
-file("src/oaspec/codegen/writer.gleam", 252).
?DOC(" Convert a write error to a human-readable string.\n").
-spec error_to_string(write_error()) -> binary().
error_to_string(Error) ->
case Error of
{directory_create_error, Path, Detail} ->
<<<<<<"Failed to create directory "/utf8, Path/binary>>/binary,
": "/utf8>>/binary,
Detail/binary>>;
{file_write_error, Path@1, Detail@1} ->
<<<<<<"Failed to write file "/utf8, Path@1/binary>>/binary,
": "/utf8>>/binary,
Detail@1/binary>>
end.