-module(packkit).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit.gleam").
-export([package_version/0, decompress_with_limits/3, decompress/2, compress/2, read_with_limits/3, read/2, write/2, detect_filename/1, detect_bytes/1, detect_path_or_bytes/2, pack/2, unpack_with_limits/3, unpack/2]).
-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(
" Facade for the `packkit` package. This module wires the codec,\n"
" archive, and recipe primitives so callers can read, write, pack,\n"
" and unpack data without selecting the underlying engine by hand.\n"
).
-file("src/packkit.gleam", 60).
?DOC(" The package version.\n").
-spec package_version() -> binary().
package_version() ->
<<"0.1.0"/utf8>>.
-file("src/packkit.gleam", 165).
-spec enforce_input_limit(bitstring(), packkit@limit:limits()) -> {ok, nil} |
{error, packkit@error:codec_error()}.
enforce_input_limit(Bytes, Limits) ->
Size = erlang:byte_size(Bytes),
case Size > packkit@limit:max_input_bytes(Limits) of
true ->
{error, {codec_limit_exceeded, <<"max_input_bytes"/utf8>>, Size}};
false ->
{ok, nil}
end.
-file("src/packkit.gleam", 219).
-spec decompress_zlib(
bitstring(),
packkit@codec:codec(),
packkit@limit:limits()
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
decompress_zlib(Bytes, Codec_value, Limits) ->
case packkit@codec:dictionary_of(Codec_value) of
none ->
packkit@zlib:decode_with_limits(Bytes, Limits);
{some, Dict} ->
packkit@zlib:decode_with_dictionary_and_limits(
Bytes,
packkit@codec:dictionary_bytes(Dict),
Limits
)
end.
-file("src/packkit.gleam", 294).
-spec reject_dictionary(packkit@codec:codec()) -> {ok, nil} |
{error, packkit@error:codec_error()}.
reject_dictionary(Codec_value) ->
case packkit@codec:dictionary_of(Codec_value) of
none ->
{ok, nil};
{some, _} ->
case packkit@codec:name(Codec_value) of
<<"zlib"/utf8>> ->
{ok, nil};
Name ->
{error,
{codec_option_unsupported, <<"dictionary"/utf8>>, Name}}
end
end.
-file("src/packkit.gleam", 309).
-spec reject_level(packkit@codec:codec(), binary()) -> {ok, nil} |
{error, packkit@error:codec_error()}.
reject_level(Codec_value, Codec_name) ->
case packkit@codec:level(Codec_value) of
none ->
{ok, nil};
{some, _} ->
{error, {codec_option_unsupported, <<"level"/utf8>>, Codec_name}}
end.
-file("src/packkit.gleam", 266).
?DOC(
" Codecs whose encoders genuinely cannot consume a level knob — any\n"
" non-`None` level (other than the implicit default) is reported as\n"
" `CodecOptionUnsupported` so callers see the mismatch instead of\n"
" the codec silently doing the same thing for every level.\n"
).
-spec compress_levelless(
bitstring(),
packkit@codec:codec(),
binary(),
fun((bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()})
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
compress_levelless(Bytes, Codec_value, Codec_name, Run) ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) ->
gleam@result:'try'(
reject_level(Codec_value, Codec_name),
fun(_) -> Run(Bytes) end
)
end
).
-file("src/packkit.gleam", 345).
-spec default_level_value() -> integer().
default_level_value() ->
packkit@level:value(packkit@level:default()).
-file("src/packkit.gleam", 327).
?DOC(
" Accepts the implicit default level, rejects anything else with\n"
" `CodecOptionUnsupported`. Used by codecs whose encoders share a\n"
" single fixed strategy, so non-default levels would be silently\n"
" dropped if accepted.\n"
).
-spec reject_non_default_level(packkit@codec:codec(), binary()) -> {ok, nil} |
{error, packkit@error:codec_error()}.
reject_non_default_level(Codec_value, Codec_name) ->
case packkit@codec:level(Codec_value) of
none ->
{ok, nil};
{some, L} ->
case packkit@level:value(L) =:= default_level_value() of
true ->
{ok, nil};
false ->
{error,
{codec_option_unsupported, <<"level"/utf8>>, Codec_name}}
end
end.
-file("src/packkit.gleam", 112).
?DOC(
" Decompress `bytes` with `codec`, threading the supplied `Limits`\n"
" value through to the underlying codec's `decode_with_limits`\n"
" entrypoint. Codecs without an explicit limits hook receive their\n"
" own family-specific defaults; today every byte-to-byte codec we\n"
" support honours `Limits`.\n"
).
-spec decompress_with_limits(
bitstring(),
packkit@codec:codec(),
packkit@limit:limits()
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
decompress_with_limits(Bytes, Codec_value, Limits) ->
case packkit@codec:kind(Codec_value) of
identity ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) ->
gleam@result:'try'(
reject_non_default_level(
Codec_value,
<<"identity"/utf8>>
),
fun(_) -> _pipe = enforce_input_limit(Bytes, Limits),
gleam@result:map(_pipe, fun(_) -> Bytes end) end
)
end
);
deflate ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@deflate:decode_with_limits(Bytes, Limits) end
);
zlib ->
decompress_zlib(Bytes, Codec_value, Limits);
gzip ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) ->
_pipe@1 = packkit@gzip:decode_with_limits(Bytes, Limits),
gleam@result:map(
_pipe@1,
fun(Decoded) -> erlang:element(3, Decoded) end
)
end
);
lz4 ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@lz4:decode_with_limits(Bytes, Limits) end
);
snappy ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@snappy:decode_with_limits(Bytes, Limits) end
);
bzip2 ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@bzip2:decode_with_limits(Bytes, Limits) end
);
lzw ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@lzw:decode_with_limits(Bytes, Limits) end
);
xz ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@xz:decode_with_limits(Bytes, Limits) end
);
zstd ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@zstd:decode_with_limits(Bytes, Limits) end
);
brotli ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> packkit@brotli:decode_with_limits(Bytes, Limits) end
)
end.
-file("src/packkit.gleam", 96).
?DOC(
" Decompress `bytes` with `codec` using the default limits. Honours\n"
" the codec's optional preset dictionary (currently only zlib) and\n"
" otherwise rejects dictionary use with a typed error.\n"
).
-spec decompress(bitstring(), packkit@codec:codec()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
decompress(Bytes, Codec_value) ->
decompress_with_limits(Bytes, Codec_value, packkit@limit:default()).
-file("src/packkit.gleam", 200).
-spec compress_zlib(bitstring(), packkit@codec:codec()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
compress_zlib(Bytes, Codec_value) ->
gleam@result:'try'(
reject_non_default_level(Codec_value, <<"zlib"/utf8>>),
fun(_) -> case packkit@codec:dictionary_of(Codec_value) of
none ->
packkit@zlib:encode(Bytes);
{some, Dict} ->
packkit@zlib:encode_with_dictionary(
Bytes,
packkit@codec:dictionary_bytes(Dict)
)
end end
).
-file("src/packkit.gleam", 235).
-spec compress_gzip(bitstring(), packkit@codec:codec()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
compress_gzip(Bytes, Codec_value) ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) ->
gleam@result:'try'(
reject_non_default_level(Codec_value, <<"gzip"/utf8>>),
fun(_) -> packkit@gzip:encode(Bytes) end
)
end
).
-file("src/packkit.gleam", 283).
?DOC(
" Codecs whose encoders accept a level conceptually but currently\n"
" always emit the simplest representation (xz LZMA2 uncompressed,\n"
" zstd raw frames, brotli uncompressed metablocks). We accept the\n"
" implicit default level (so the smart constructor still works) but\n"
" reject any caller-supplied non-default level so it's never\n"
" silently dropped. Dictionaries are still rejected.\n"
).
-spec compress_fixed_level(
bitstring(),
packkit@codec:codec(),
binary(),
fun((bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()})
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
compress_fixed_level(Bytes, Codec_value, Codec_name, Run) ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) ->
gleam@result:'try'(
reject_non_default_level(Codec_value, Codec_name),
fun(_) -> Run(Bytes) end
)
end
).
-file("src/packkit.gleam", 349).
-spec effective_level(packkit@codec:codec()) -> gleam@option:option(integer()).
effective_level(Codec_value) ->
case packkit@codec:level(Codec_value) of
{some, L} ->
{some, packkit@level:value(L)};
none ->
none
end.
-file("src/packkit.gleam", 177).
-spec compress_deflate(bitstring(), packkit@codec:codec()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
compress_deflate(Bytes, Codec_value) ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) -> case effective_level(Codec_value) of
none ->
packkit@deflate:encode(Bytes);
{some, 0} ->
packkit@deflate:encode_stored_only(Bytes);
{some, N} ->
case N =:= default_level_value() of
true ->
packkit@deflate:encode(Bytes);
false ->
{error,
{codec_option_unsupported,
<<"level"/utf8>>,
<<"deflate"/utf8>>}}
end
end end
).
-file("src/packkit.gleam", 356).
-spec int_clamp(integer(), integer(), integer()) -> integer().
int_clamp(Value, Low, High) ->
case {Value < Low, Value > High} of
{true, _} ->
Low;
{_, true} ->
High;
{_, _} ->
Value
end.
-file("src/packkit.gleam", 246).
-spec compress_bzip2(bitstring(), packkit@codec:codec()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
compress_bzip2(Bytes, Codec_value) ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) ->
Level_value = case effective_level(Codec_value) of
{some, 0} ->
1;
{some, N} ->
int_clamp(N, 1, 9);
none ->
9
end,
packkit@bzip2:encode_with_level(Bytes, Level_value)
end
).
-file("src/packkit.gleam", 68).
?DOC(
" Compress `bytes` with `codec`. The codec's optional level and\n"
" preset dictionary are honoured where the family supports them; if\n"
" the family cannot honour an option a typed `CodecOptionUnsupported`\n"
" error is returned instead of silently dropping the request.\n"
).
-spec compress(bitstring(), packkit@codec:codec()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
compress(Bytes, Codec_value) ->
case packkit@codec:kind(Codec_value) of
identity ->
gleam@result:'try'(
reject_dictionary(Codec_value),
fun(_) ->
gleam@result:'try'(
reject_non_default_level(
Codec_value,
<<"identity"/utf8>>
),
fun(_) -> {ok, Bytes} end
)
end
);
deflate ->
compress_deflate(Bytes, Codec_value);
zlib ->
compress_zlib(Bytes, Codec_value);
gzip ->
compress_gzip(Bytes, Codec_value);
lz4 ->
compress_levelless(
Bytes,
Codec_value,
<<"lz4"/utf8>>,
fun packkit@lz4:encode/1
);
snappy ->
compress_levelless(
Bytes,
Codec_value,
<<"snappy"/utf8>>,
fun packkit@snappy:encode/1
);
bzip2 ->
compress_bzip2(Bytes, Codec_value);
lzw ->
compress_levelless(
Bytes,
Codec_value,
<<"lzw"/utf8>>,
fun packkit@lzw:encode/1
);
xz ->
compress_fixed_level(
Bytes,
Codec_value,
<<"xz"/utf8>>,
fun packkit@xz:encode/1
);
zstd ->
compress_fixed_level(
Bytes,
Codec_value,
<<"zstd"/utf8>>,
fun packkit@zstd:encode/1
);
brotli ->
compress_fixed_level(
Bytes,
Codec_value,
<<"brotli"/utf8>>,
fun packkit@brotli:encode/1
)
end.
-file("src/packkit.gleam", 377).
?DOC(
" Read an archive while threading the supplied `Limits` through to\n"
" the underlying archive decoder. Each family enforces the subset of\n"
" limits that applies to it (input size, member count, name length,\n"
" entry depth).\n"
).
-spec read_with_limits(
bitstring(),
packkit@archive:archive_format(),
packkit@limit:limits()
) -> {ok, packkit@archive:archive()} | {error, packkit@error:archive_error()}.
read_with_limits(Bytes, Format, Limits) ->
case packkit@archive:kind(Format) of
tar ->
packkit@tar:decode_with_limits(Bytes, Limits);
zip ->
packkit@zip:decode_with_limits(Bytes, Limits);
cpio_newc ->
packkit@cpio:decode_with_limits(Bytes, Limits);
ar ->
packkit@ar:decode_with_limits(Bytes, Limits);
seven_z ->
packkit@seven_z:decode_with_limits(Bytes, Limits)
end.
-file("src/packkit.gleam", 366).
?DOC(
" Read an archive from `bytes` interpreted as `format` using the\n"
" default resource limits.\n"
).
-spec read(bitstring(), packkit@archive:archive_format()) -> {ok,
packkit@archive:archive()} |
{error, packkit@error:archive_error()}.
read(Bytes, Format) ->
read_with_limits(Bytes, Format, packkit@limit:default()).
-file("src/packkit.gleam", 410).
-spec ensure_archive_format_matches(
packkit@archive:archive(),
packkit@archive:archive_format()
) -> {ok, nil} | {error, packkit@error:archive_error()}.
ensure_archive_format_matches(Archive_value, Requested) ->
Archive_kind = packkit@archive:kind(packkit@archive:format(Archive_value)),
Requested_kind = packkit@archive:kind(Requested),
case Archive_kind =:= Requested_kind of
true ->
{ok, nil};
false ->
{error,
{archive_format_mismatch,
packkit@archive:name(packkit@archive:format(Archive_value)),
packkit@archive:name(Requested)}}
end.
-file("src/packkit.gleam", 396).
?DOC(
" Serialise an archive to bytes using `format`. The supplied\n"
" `format` must match the format tag the `Archive` was constructed\n"
" with — `Archive` is bound to one format at construction time, and\n"
" pretending it is a different format would silently corrupt the\n"
" output. Mismatches surface as `ArchiveFormatMismatch`.\n"
).
-spec write(packkit@archive:archive(), packkit@archive:archive_format()) -> {ok,
bitstring()} |
{error, packkit@error:archive_error()}.
write(Archive_value, Format) ->
gleam@result:'try'(
ensure_archive_format_matches(Archive_value, Format),
fun(_) -> case packkit@archive:kind(Format) of
tar ->
packkit@tar:encode(Archive_value);
zip ->
packkit@zip:encode(Archive_value);
cpio_newc ->
packkit@cpio:encode(Archive_value);
ar ->
packkit@ar:encode(Archive_value);
seven_z ->
packkit@seven_z:encode(Archive_value)
end end
).
-file("src/packkit.gleam", 474).
?DOC(" Detect from a filename or path suffix.\n").
-spec detect_filename(binary()) -> {ok, packkit@detect:detected()} |
{error, packkit@error:detect_error()}.
detect_filename(Path) ->
packkit@detect:from_filename(Path).
-file("src/packkit.gleam", 479).
?DOC(" Detect from byte signatures.\n").
-spec detect_bytes(bitstring()) -> {ok, packkit@detect:detected()} |
{error, packkit@error:detect_error()}.
detect_bytes(Bytes) ->
packkit@detect:from_bytes(Bytes).
-file("src/packkit.gleam", 487).
?DOC(
" Detect via filename first, then fall back to magic-byte detection\n"
" on the supplied content. Re-exports\n"
" `packkit/detect.from_path_or_bytes` from the top-level facade so\n"
" most CLI integrations only need to import `packkit`.\n"
).
-spec detect_path_or_bytes(binary(), bitstring()) -> {ok,
packkit@detect:detected()} |
{error, packkit@error:detect_error()}.
detect_path_or_bytes(Path, Bytes) ->
packkit@detect:from_path_or_bytes(Path, Bytes).
-file("src/packkit.gleam", 494).
-spec apply_codec_chain_forward(bitstring(), list(packkit@codec:codec())) -> {ok,
bitstring()} |
{error, packkit@error:codec_error()}.
apply_codec_chain_forward(Bytes, Codecs) ->
case Codecs of
[] ->
{ok, Bytes};
[Head | Rest] ->
gleam@result:'try'(
compress(Bytes, Head),
fun(Compressed) ->
apply_codec_chain_forward(Compressed, Rest)
end
)
end.
-file("src/packkit.gleam", 507).
-spec apply_codec_chain_reverse(
bitstring(),
list(packkit@codec:codec()),
packkit@limit:limits()
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
apply_codec_chain_reverse(Bytes, Codecs, Limits) ->
case Codecs of
[] ->
{ok, Bytes};
[Head | Rest] ->
gleam@result:'try'(
decompress_with_limits(Bytes, Head, Limits),
fun(Plain) -> apply_codec_chain_reverse(Plain, Rest, Limits) end
)
end.
-file("src/packkit.gleam", 525).
-spec codec_to_archive_error(
{ok, WIG} | {error, packkit@error:codec_error()},
binary()
) -> {ok, WIG} | {error, packkit@error:archive_error()}.
codec_to_archive_error(Value, Step) ->
case Value of
{ok, V} ->
{ok, V};
{error, Err} ->
{error, {archive_codec_failed, Step, Err}}
end.
-file("src/packkit.gleam", 427).
?DOC(" Pack an archive with the codec chain described by `recipe`.\n").
-spec pack(packkit@archive:archive(), packkit@recipe:recipe()) -> {ok,
bitstring()} |
{error, packkit@error:archive_error()}.
pack(Archive_value, Recipe_value) ->
gleam@result:'try'(
write(Archive_value, packkit@recipe:archive_format(Recipe_value)),
fun(Archive_bytes) ->
_pipe = apply_codec_chain_forward(
Archive_bytes,
packkit@recipe:codecs(Recipe_value)
),
codec_to_archive_error(_pipe, <<"encode"/utf8>>)
end
).
-file("src/packkit.gleam", 452).
?DOC(
" Unpack a byte stream produced by `recipe`, threading the supplied\n"
" `Limits` through both the codec chain and the underlying archive\n"
" decoder.\n"
).
-spec unpack_with_limits(
bitstring(),
packkit@recipe:recipe(),
packkit@limit:limits()
) -> {ok, packkit@archive:archive()} | {error, packkit@error:archive_error()}.
unpack_with_limits(Bytes, Recipe_value, Limits) ->
gleam@result:'try'(
begin
_pipe = apply_codec_chain_reverse(
Bytes,
lists:reverse(packkit@recipe:codecs(Recipe_value)),
Limits
),
codec_to_archive_error(_pipe, <<"decode"/utf8>>)
end,
fun(Raw_bytes) ->
read_with_limits(
Raw_bytes,
packkit@recipe:archive_format(Recipe_value),
Limits
)
end
).
-file("src/packkit.gleam", 442).
?DOC(
" Unpack a byte stream produced by `recipe` using the default\n"
" resource limits.\n"
).
-spec unpack(bitstring(), packkit@recipe:recipe()) -> {ok,
packkit@archive:archive()} |
{error, packkit@error:archive_error()}.
unpack(Bytes, Recipe_value) ->
unpack_with_limits(Bytes, Recipe_value, packkit@limit:default()).