# `Stevedore.Archive`
[🔗](https://github.com/oshlabs/stevedore/blob/v0.2.0/lib/stevedore/archive.ex#L1)

A pure tar reader/writer plus gzip helpers.

OCI layers are tar archives (optionally gzip- or zstd-compressed). This reads and writes them
without shelling out to `tar`: the writer emits POSIX **ustar** headers; the reader also
understands GNU long-name (`L`) and PAX extended-header (`x`) records, which real registry
images use for paths longer than 100 bytes. zstd is added in a later phase (optional NIF);
gzip is native via Erlang's `:zlib`.

Spec: [POSIX pax/ustar format](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06).

# `entry`

```elixir
@type entry() :: %{
  name: String.t(),
  type: :regular | :directory | :symlink | :hardlink,
  mode: non_neg_integer(),
  size: non_neg_integer(),
  linkname: String.t() | nil,
  content: binary() | nil
}
```

# `gunzip`

```elixir
@spec gunzip(binary()) :: {:ok, binary()} | {:error, :gzip}
```

Decompresses gzip `data`, returning `{:error, :gzip}` on malformed input.

# `gzip`

```elixir
@spec gzip(iodata()) :: binary()
```

Gzip-compresses `data` (RFC 1952).

## Examples

    iex> Stevedore.Archive.gunzip(Stevedore.Archive.gzip("payload"))
    {:ok, "payload"}

# `read`

```elixir
@spec read(binary()) :: {:ok, [entry()]} | {:error, Stevedore.Archive.Error.t()}
```

Reads a tar archive into a list of entries, in archive order.

## Examples

    iex> tar = Stevedore.Archive.write!([%{name: "a.txt", type: :regular, mode: 0o644, size: 2, linkname: nil, content: "hi"}])
    iex> {:ok, [entry]} = Stevedore.Archive.read(tar)
    iex> {entry.name, entry.content}
    {"a.txt", "hi"}

# `unzstd`

```elixir
@spec unzstd(binary()) :: {:ok, binary()} | {:error, :zstd}
```

Decompresses zstd `data`. Raises a clear error if the optional `:ezstd` NIF isn't available.

# `write`

```elixir
@spec write([entry()]) :: {:ok, binary()} | {:error, Stevedore.Archive.Error.t()}
```

Writes entries to a tar archive, returning `{:ok, binary}` or an error tuple.

## Examples

    iex> {:ok, tar} = Stevedore.Archive.write([%{name: "a", type: :regular, mode: 0o644, size: 0, linkname: nil, content: ""}])
    iex> is_binary(tar)
    true

# `write!`

```elixir
@spec write!([entry()]) :: binary()
```

Writes entries to a tar archive (ustar). Raises `Stevedore.Archive.Error` on an unencodable
entry (e.g. a path too long for ustar headers).

## Examples

    iex> tar = Stevedore.Archive.write!([%{name: "d", type: :directory, mode: 0o755, size: 0, linkname: nil, content: nil}])
    iex> {:ok, [entry]} = Stevedore.Archive.read(tar)
    iex> entry.type
    :directory

# `zstd`

```elixir
@spec zstd(iodata()) :: binary()
```

Zstd-compresses `data`. Raises a clear error if the optional `:ezstd` NIF isn't available.

# `zstd_available?`

```elixir
@spec zstd_available?() :: boolean()
```

Whether zstd support is available — i.e. the optional `:ezstd` NIF is loaded. gzip is always
available natively; zstd requires adding `{:ezstd, "~> 1.1"}` to your deps.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
