# API de S3

Os utilitários de armazenamento de objetos compatíveis com S3 estão disponíveis em `ptool.s3` e `p.s3`.

## ptool.s3.connect

> `v0.10.0` - Introduzido.

`ptool.s3.connect(options)` abre uma conexão de armazenamento de objetos compatível com S3 e retorna um objeto `Connection`.

Campos de `options`:

- `bucket` (string, obrigatório): O nome do bucket.
- `region` (string, opcional): A região da AWS ou do provedor.
- `endpoint` (string, opcional): Uma URL de endpoint compatível com S3 personalizada, como MinIO, R2 ou outro serviço de armazenamento de objetos.
- `access_key_id` (string, opcional): O ID da access key.
- `secret_access_key` (string, opcional): A secret access key.
- `session_token` (string, opcional): O token de sessão.
- `root` (string, opcional): Um prefixo raiz aplicado a todas as operações com objetos.
- `allow_anonymous` (boolean, opcional): Quando `true`, permite requisições sem assinatura se as credenciais não estiverem configuradas. O padrão é `false`.

Fallback de ambiente:

- Valores explícitos em `options` têm prioridade.
- Valores ausentes de `region`, `endpoint`, `access_key_id`, `secret_access_key` e `session_token` usam fallback para:
  - `AWS_REGION`
  - `AWS_ENDPOINT`, `AWS_ENDPOINT_URL` ou `AWS_S3_ENDPOINT`
  - `AWS_ACCESS_KEY_ID`
  - `AWS_SECRET_ACCESS_KEY`
  - `AWS_SESSION_TOKEN`
- O fallback de ambiente usa a visão do ambiente de runtime do `ptool`, então valores definidos com `p.os.setenv(...)` também ficam visíveis para `ptool.s3.connect(...)`.

Exemplo:

```lua
local s3 = ptool.s3.connect({
  bucket = "artifacts",
  region = "auto",
  endpoint = "https://<account>.r2.cloudflarestorage.com",
  access_key_id = p.os.getenv("AWS_ACCESS_KEY_ID"),
  secret_access_key = p.os.getenv("AWS_SECRET_ACCESS_KEY"),
  root = "builds/",
})
```

## Connection

> `v0.10.0` - Introduzido.

`Connection` representa uma conexão aberta de armazenamento de objetos retornada por `ptool.s3.connect()`.

Ela é implementada como um userdata Lua.

Métodos:

- `conn:read(path)` -> `string`
- `conn:write(path, content)` -> `nil`
- `conn:delete(path)` -> `nil`
- `conn:exists(path)` -> `boolean`
- `conn:list([prefix])` -> `table`
- `conn:stat(path)` -> `table`

Regras de caminho:

- Os caminhos de objetos devem ser strings não vazias, salvo indicação em contrário.
- A barra inicial `/` é ignorada, então `/foo/bar.txt` e `foo/bar.txt` apontam para o mesmo objeto.
- Os caminhos são relativos a `root` quando `root` está configurado na conexão.

Estrutura da tabela de entrada:

- `path` (string): O caminho do objeto relativo à raiz da conexão.
- `size` (integer): O tamanho do objeto em bytes.
- `etag` (string | nil): O ETag do objeto, quando disponível.
- `last_modified` (string | nil): O timestamp da última modificação, quando disponível.
- `content_type` (string | nil): O tipo de conteúdo do objeto, quando disponível.
- `is_file` (boolean): Se a entrada é um arquivo.
- `is_dir` (boolean): Se a entrada é um diretório.
- `mode` (string): Um entre `"file"`, `"dir"` ou `"unknown"`.

### read

> `v0.10.0` - Introduzido.

Nome canônico da API: `ptool.s3.Connection:read`.

`conn:read(path)` lê um objeto como bytes brutos e retorna uma string Lua.

- `path` (string, obrigatório): O caminho do objeto.
- Retorna: `string`.

Exemplo:

```lua
local s3 = ptool.s3.connect({ bucket = "artifacts" })
local content = s3:read("releases/v1.0.0/notes.txt")
print(content)
```

### write

> `v0.10.0` - Introduzido.

Nome canônico da API: `ptool.s3.Connection:write`.

`conn:write(path, content)` grava uma string Lua em um objeto como bytes brutos.

- `path` (string, obrigatório): O caminho do objeto.
- `content` (string, obrigatório): Os bytes a enviar.

Comportamento:

- `content` é enviado byte a byte.
- Bytes NUL embutidos e bytes não UTF-8 são preservados.

Exemplo:

```lua
local s3 = ptool.s3.connect({ bucket = "artifacts" })
s3:write("tmp/hello.txt", "hello\n")
s3:write("tmp/blob.bin", "\x00\xffABC")
```

### delete

> `v0.10.0` - Introduzido.

Nome canônico da API: `ptool.s3.Connection:delete`.

`conn:delete(path)` exclui um objeto.

- `path` (string, obrigatório): O caminho do objeto.

### exists

> `v0.10.0` - Introduzido.

Nome canônico da API: `ptool.s3.Connection:exists`.

`conn:exists(path)` verifica se um objeto existe.

- `path` (string, obrigatório): O caminho do objeto.
- Retorna: `boolean`.

### list

> `v0.10.0` - Introduzido.

Nome canônico da API: `ptool.s3.Connection:list`.

`conn:list([prefix])` lista entradas sob um prefixo e retorna uma tabela array Lua densa.

- `prefix` (string, opcional): O prefixo a listar. O padrão é a raiz da conexão.
- Retorna: `table`.

Exemplo:

```lua
local s3 = ptool.s3.connect({ bucket = "artifacts", root = "builds/" })
local entries = s3:list("2026/")

for _, entry in ipairs(entries) do
  print(entry.path, entry.mode, entry.size)
end
```

### stat

> `v0.10.0` - Introduzido.

Nome canônico da API: `ptool.s3.Connection:stat`.

`conn:stat(path)` retorna metadados de um único objeto.

- `path` (string, obrigatório): O caminho do objeto.
- Retorna: `table`.

Exemplo:

```lua
local s3 = ptool.s3.connect({ bucket = "artifacts" })
local meta = s3:stat("releases/v1.0.0/app.tar.zst")
print(meta.size, meta.etag, meta.last_modified)
```
