# `HTTP.FormData`
[🔗](https://github.com/gsmlg-dev/http_fetch/blob/v0.16.7/apps/http_core/lib/http/form_data.ex#L1)

HTTP form data and multipart/form-data encoding for file uploads.

This module provides a convenient API for building form submissions with support
for both URL-encoded forms and multipart file uploads. It automatically chooses
the appropriate encoding based on the presence of file fields.

## Encoding Selection

- **URL-encoded** (`application/x-www-form-urlencoded`): Used when form contains only text fields
- **Multipart** (`multipart/form-data`): Used when form contains file fields

## Features

- **File stream inputs**: Accepts `File.Stream` values when building multipart bodies
- **Automatic encoding**: Selects appropriate encoding based on content
- **Boundary generation**: Automatically generates unique multipart boundaries
- **Mixed content**: Support for both text fields and files in the same form

## Basic Usage

    # Simple form with text fields
    form = HTTP.FormData.new()
           |> HTTP.FormData.append_field("username", "john_doe")
           |> HTTP.FormData.append_field("email", "john@example.com")

    HTTP.fetch("https://api.example.com/signup", method: "POST", body: form)

## File Upload

    # Single file upload
    file_stream = File.stream!("document.pdf")
    form = HTTP.FormData.new()
           |> HTTP.FormData.append_field("title", "My Document")
           |> HTTP.FormData.append_file("document", "document.pdf", file_stream, "application/pdf")

    HTTP.fetch("https://api.example.com/upload", method: "POST", body: form)

## Multiple Files

    # Upload multiple files
    form = HTTP.FormData.new()
           |> HTTP.FormData.append_field("description", "Photos from vacation")
           |> HTTP.FormData.append_file("photo1", "beach.jpg", File.stream!("beach.jpg"), "image/jpeg")
           |> HTTP.FormData.append_file("photo2", "sunset.jpg", File.stream!("sunset.jpg"), "image/jpeg")

    HTTP.fetch("https://api.example.com/gallery", method: "POST", body: form)

## Content Types

When uploading files, you can specify the MIME type. If not provided, it defaults
to `"application/octet-stream"`:

    # With explicit content type
    form |> HTTP.FormData.append_file("image", "photo.jpg", stream, "image/jpeg")

    # With default content type
    form |> HTTP.FormData.append_file("data", "data.bin", stream)

# `form_part`

```elixir
@type form_part() ::
  {:field, String.t(), String.t()}
  | {:file, String.t(), String.t(), String.t(), File.Stream.t()}
  | {:file, String.t(), String.t(), String.t(), String.t()}
```

# `t`

```elixir
@type t() :: %HTTP.FormData{boundary: String.t() | nil, parts: [form_part()]}
```

# `append_field`

```elixir
@spec append_field(t(), String.t(), String.t()) :: t()
```

Adds a form field.

## Examples

    iex> HTTP.FormData.new() |> HTTP.FormData.append_field("name", "value")
    %HTTP.FormData{parts: [{:field, "name", "value"}], boundary: nil}

# `append_file`

```elixir
@spec append_file(
  t(),
  String.t(),
  String.t(),
  File.Stream.t() | String.t(),
  String.t()
) :: t()
```

Adds a file field for upload.

## Examples

    iex> file_stream = File.stream!("test.txt")
    iex> HTTP.FormData.new() |> HTTP.FormData.append_file("upload", "test.txt", file_stream)
    %HTTP.FormData{parts: [{:file, "upload", "test.txt", "text/plain", %File.Stream{}}], boundary: nil}

# `generate_boundary`

```elixir
@spec generate_boundary() :: String.t()
```

Generates a random boundary for multipart/form-data.

# `get_content_type`

```elixir
@spec get_content_type(t()) :: String.t()
```

Gets the appropriate Content-Type header for the form data.

# `new`

```elixir
@spec new() :: t()
```

Creates a new empty FormData struct.

## Examples

    iex> HTTP.FormData.new()
    %HTTP.FormData{parts: [], boundary: nil}

# `to_body`

```elixir
@spec to_body(t()) :: {:url_encoded, String.t()} | {:multipart, iodata(), String.t()}
```

Converts FormData to HTTP body content with appropriate encoding.

Returns {:url_encoded, body} for regular forms or {:multipart, body, boundary} for multipart.
The multipart body is returned as iodata for memory efficiency with large file uploads.

---

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