-module(packkit@archive).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/archive.gleam").
-export([tar/0, zip/0, seven_z/0, cpio_newc/0, ar/0, new/1, from_entries/2, add/2, add_file_checked/3, add_file/3, add_directory_checked/2, add_directory/2, add_symlink_checked/3, add_symlink/3, add_hardlink_checked/3, add_hardlink/3, with_comment/2, format/1, entries/1, entry_by_path/2, comment/1, entry_count/1, kind/1, name/1]).
-export_type([archive_kind/0, archive_format/0, archive/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 archive_kind() :: tar | zip | seven_z | cpio_newc | ar.
-opaque archive_format() :: {archive_format, archive_kind()}.
-opaque archive() :: {archive,
archive_format(),
list(packkit@entry:entry()),
gleam@option:option(binary())}.
-file("src/packkit/archive.gleam", 35).
?DOC(" Tar archive format.\n").
-spec tar() -> archive_format().
tar() ->
{archive_format, tar}.
-file("src/packkit/archive.gleam", 40).
?DOC(" ZIP archive format.\n").
-spec zip() -> archive_format().
zip() ->
{archive_format, zip}.
-file("src/packkit/archive.gleam", 45).
?DOC(" 7z archive format.\n").
-spec seven_z() -> archive_format().
seven_z() ->
{archive_format, seven_z}.
-file("src/packkit/archive.gleam", 50).
?DOC(" `cpio` newc archive format.\n").
-spec cpio_newc() -> archive_format().
cpio_newc() ->
{archive_format, cpio_newc}.
-file("src/packkit/archive.gleam", 55).
?DOC(" Unix `ar` archive format.\n").
-spec ar() -> archive_format().
ar() ->
{archive_format, ar}.
-file("src/packkit/archive.gleam", 60).
?DOC(" Create an empty archive value for the supplied format.\n").
-spec new(archive_format()) -> archive().
new(Format) ->
{archive, Format, [], none}.
-file("src/packkit/archive.gleam", 65).
?DOC(" Create an archive from a full entry list.\n").
-spec from_entries(archive_format(), list(packkit@entry:entry())) -> archive().
from_entries(Format, Entries) ->
{archive, Format, lists:reverse(Entries), none}.
-file("src/packkit/archive.gleam", 79).
?DOC(
" Append an entry to the logical archive. Runs in O(1) by prepending\n"
" to a reversed internal list; observable order is preserved through\n"
" [entries].\n"
).
-spec add(archive(), packkit@entry:entry()) -> archive().
add(Archive, Entry) ->
{archive,
erlang:element(2, Archive),
[Entry | erlang:element(3, Archive)],
erlang:element(4, Archive)}.
-file("src/packkit/archive.gleam", 88).
?DOC(
" Add a regular file after checked path validation. Format-agnostic\n"
" counterpart to `tar.add_file_checked`; works for any archive\n"
" produced by `new(format:)`. Format-side restrictions (e.g. `ar`\n"
" only carries flat files, `7z`'s encoder is not yet implemented)\n"
" surface at encode time as `ArchiveError`.\n"
).
-spec add_file_checked(archive(), binary(), bitstring()) -> {ok, archive()} |
{error, packkit@entry:entry_error()}.
add_file_checked(Archive_value, Path, Body) ->
case packkit@entry:file_checked(Path, Body) of
{ok, Value} ->
{ok, add(Archive_value, Value)};
{error, Err} ->
{error, Err}
end.
-file("src/packkit/archive.gleam", 100).
?DOC(" Panicking counterpart of `add_file_checked`.\n").
-spec add_file(archive(), binary(), bitstring()) -> archive().
add_file(Archive_value, Path, Body) ->
add(Archive_value, packkit@entry:file(Path, Body)).
-file("src/packkit/archive.gleam", 109).
?DOC(" Add a directory after checked path validation.\n").
-spec add_directory_checked(archive(), binary()) -> {ok, archive()} |
{error, packkit@entry:entry_error()}.
add_directory_checked(Archive_value, Path) ->
case packkit@entry:directory_checked(Path) of
{ok, Value} ->
{ok, add(Archive_value, Value)};
{error, Err} ->
{error, Err}
end.
-file("src/packkit/archive.gleam", 120).
?DOC(" Panicking counterpart of `add_directory_checked`.\n").
-spec add_directory(archive(), binary()) -> archive().
add_directory(Archive_value, Path) ->
add(Archive_value, packkit@entry:directory(Path)).
-file("src/packkit/archive.gleam", 131).
?DOC(
" Add a symbolic link after checked path validation. Formats whose\n"
" on-disk layout has no symlink slot (e.g. ZIP without the unix\n"
" extra field, ar) will reject the archive at encode time; the\n"
" logical archive value can still carry the entry.\n"
).
-spec add_symlink_checked(archive(), binary(), binary()) -> {ok, archive()} |
{error, packkit@entry:entry_error()}.
add_symlink_checked(Archive_value, Path, Target) ->
case packkit@entry:symlink_checked(Path, Target) of
{ok, Value} ->
{ok, add(Archive_value, Value)};
{error, Err} ->
{error, Err}
end.
-file("src/packkit/archive.gleam", 143).
?DOC(" Panicking counterpart of `add_symlink_checked`.\n").
-spec add_symlink(archive(), binary(), binary()) -> archive().
add_symlink(Archive_value, Path, Target) ->
add(Archive_value, packkit@entry:symlink(Path, Target)).
-file("src/packkit/archive.gleam", 153).
?DOC(
" Add a hard link after checked path validation. Formats without\n"
" hard-link support reject the archive at encode time.\n"
).
-spec add_hardlink_checked(archive(), binary(), binary()) -> {ok, archive()} |
{error, packkit@entry:entry_error()}.
add_hardlink_checked(Archive_value, Path, Target) ->
case packkit@entry:hardlink_checked(Path, Target) of
{ok, Value} ->
{ok, add(Archive_value, Value)};
{error, Err} ->
{error, Err}
end.
-file("src/packkit/archive.gleam", 165).
?DOC(" Panicking counterpart of `add_hardlink_checked`.\n").
-spec add_hardlink(archive(), binary(), binary()) -> archive().
add_hardlink(Archive_value, Path, Target) ->
add(Archive_value, packkit@entry:hardlink(Path, Target)).
-file("src/packkit/archive.gleam", 174).
?DOC(" Attach an optional archive comment.\n").
-spec with_comment(archive(), binary()) -> archive().
with_comment(Archive, Comment) ->
{archive,
erlang:element(2, Archive),
erlang:element(3, Archive),
{some, Comment}}.
-file("src/packkit/archive.gleam", 179).
?DOC(" Read the archive format marker.\n").
-spec format(archive()) -> archive_format().
format(Archive) ->
erlang:element(2, Archive).
-file("src/packkit/archive.gleam", 184).
?DOC(" Read the logical entries in insertion order.\n").
-spec entries(archive()) -> list(packkit@entry:entry()).
entries(Archive) ->
lists:reverse(erlang:element(3, Archive)).
-file("src/packkit/archive.gleam", 206).
?DOC(
" Look up the first entry whose path matches `path`, in observable\n"
" insertion order. Returns `Error(Nil)` when no entry matches —\n"
" keeps the result type close to `list.find` so callers can chain\n"
" with `result.try`. Paths are compared by their canonical string\n"
" form (`entry.to_string`), so trailing-slash normalisation done at\n"
" insert time is preserved.\n"
"\n"
" When an archive carries two entries that compare equal under\n"
" `entry.path`, this returns the **first** one inserted — the same\n"
" answer `entries(archive) |> list.find(fn(e) { entry.to_string(...)\n"
" == path })` would produce. That is the lawful pairing of `add`\n"
" with `find` (the function name says \"find first\"). Callers that\n"
" need \"last wins\" tar-extraction semantics can reverse the entry\n"
" list first.\n"
"\n"
" Use this when you want to extract a known-named entry from a\n"
" decoded archive — every CLI extractor and \"fetch one file\" use\n"
" case reinvents the alternative form.\n"
).
-spec entry_by_path(archive(), binary()) -> {ok, packkit@entry:entry()} |
{error, nil}.
entry_by_path(Archive, Path) ->
gleam@list:find(
entries(Archive),
fun(Ent) ->
packkit@entry:to_string(packkit@entry:path(Ent)) =:= Path
end
).
-file("src/packkit/archive.gleam", 216).
?DOC(" Read the optional archive comment.\n").
-spec comment(archive()) -> gleam@option:option(binary()).
comment(Archive) ->
erlang:element(4, Archive).
-file("src/packkit/archive.gleam", 221).
?DOC(" Count the logical entries.\n").
-spec entry_count(archive()) -> integer().
entry_count(Archive) ->
erlang:length(erlang:element(3, Archive)).
-file("src/packkit/archive.gleam", 226).
?DOC(" Internal tagged kind for the archive format.\n").
-spec kind(archive_format()) -> archive_kind().
kind(Format) ->
erlang:element(2, Format).
-file("src/packkit/archive.gleam", 232).
?DOC(
" Stable string name for an archive format. Kept for diagnostics\n"
" and `description` output; internal dispatch uses [kind].\n"
).
-spec name(archive_format()) -> binary().
name(Format) ->
case erlang:element(2, Format) of
tar ->
<<"tar"/utf8>>;
zip ->
<<"zip"/utf8>>;
seven_z ->
<<"7z"/utf8>>;
cpio_newc ->
<<"cpio-newc"/utf8>>;
ar ->
<<"ar"/utf8>>
end.