-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.