# `Plover.BodyStructure`
[🔗](https://github.com/codonnell/plover/blob/main/lib/plover/body_structure.ex#L1)

Traversal utilities for `%Plover.Response.BodyStructure{}` trees.

Provides functions to explore MIME message structures, find parts by
type, list attachments, and extract metadata like charset and encoding.

## Example

    # Fetch body structure
    {:ok, [msg]} = Plover.fetch(conn, "1", [:body_structure])
    bs = msg.attrs.body_structure

    # Find the text/plain part
    [{section, part}] = Plover.BodyStructure.find_parts(bs, "text/plain")

    # Get charset and encoding for decoding
    charset = Plover.BodyStructure.charset(part)    # "UTF-8"
    encoding = Plover.BodyStructure.encoding(part)  # "QUOTED-PRINTABLE"

    # List attachments
    attachments = Plover.BodyStructure.attachments(bs)
    # [%{section: "2", filename: "report.pdf", type: "APPLICATION/PDF", ...}]

# `attachments`

```elixir
@spec attachments(Plover.Response.BodyStructure.t()) :: [map()]
```

List attachments in the message.

A part is considered an attachment if its disposition is `"ATTACHMENT"`
(case-insensitive), or if it has a filename in disposition or body params
and is not `"INLINE"`.

Returns a list of maps with keys: `:section`, `:filename`, `:type`, `:size`, `:encoding`.

# `charset`

```elixir
@spec charset(Plover.Response.BodyStructure.t()) :: String.t()
```

Get the charset from a body structure's params.

Looks for the `"CHARSET"` key (case-insensitive). Returns `"US-ASCII"`
as the default per RFC 2046.

# `encoding`

```elixir
@spec encoding(Plover.Response.BodyStructure.t()) :: String.t()
```

Get the content-transfer-encoding from a body structure.

Returns `"7BIT"` as the default per RFC 2045.

# `find_parts`

```elixir
@spec find_parts(Plover.Response.BodyStructure.t(), String.t()) :: [
  {String.t(), Plover.Response.BodyStructure.t()}
]
```

Find parts matching a MIME type pattern.

Supports exact matches (`"text/plain"`) and wildcard subtypes (`"text/*"`).
Matching is case-insensitive. Returns `{section_path, part}` tuples.

# `flatten`

```elixir
@spec flatten(Plover.Response.BodyStructure.t()) :: [
  {String.t(), Plover.Response.BodyStructure.t()}
]
```

Flatten a body structure tree into a list of `{section_path, part}` tuples.

Only returns leaf parts (not multipart containers). Section paths follow
RFC 9051 rules:

- Single-part message: `""`
- Multipart children: `"1"`, `"2"`, `"3"` (1-indexed)
- Nested multipart: `"1.1"`, `"1.2"` (dot-separated)

# `get_part`

```elixir
@spec get_part(Plover.Response.BodyStructure.t(), String.t()) ::
  {:ok, Plover.Response.BodyStructure.t()} | :error
```

Get a part by its section path (e.g., `"1.2"`).

Returns `{:ok, part}` or `:error` if the path is invalid.

---

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