# mime-tree — Project Instructions for AI Agents

Standalone, published Rust crate. Parses RFC 5322 / MIME messages into a walkable,
byte-range-indexed part tree. No JMAP dependency. No async. No S/MIME crypto.

## What This Is

Given raw RFC 5322 bytes, produces:

- `ParsedMessage` containing the full MIME part tree (`ParsedPart` nodes)
- RFC 8621 §4.1.4-compatible computed views: `text_body`, `html_body`, `attachments`
- IMAP dotted-path part IDs (`"1"`, `"1.1"`, `"1.2"`)
- Byte ranges `(offset, length)` per part — for lazy blob fetch without re-parsing
- `decode_body_value()` — slices byte range, decodes transfer encoding, charset-converts via `encoding_rs`

`application/pkcs7-mime` and `application/pkcs7-signature` parts are treated as opaque
binary leaves. S/MIME processing is `smime-tree`'s concern, not this crate's.

Callers store `ParsedMessage` (it is `Serialize + Deserialize`) however they like.

## Hard Design Invariants

Do not relitigate without explicit user approval.

1. **No JMAP dependency.** `mime-tree` must not depend on `jmap-mail-types` or any other
   JMAP-specific crate. It is a general-purpose MIME parser.
2. **No S/MIME crypto.** S/MIME parts are opaque binary leaves. Decryption and verification
   are `smime-tree`'s responsibility.
3. **`mail-parser` is a private implementation detail.** No `mail-parser` types appear in
   the public API. All public types are defined in this crate.
4. **Best-effort parsing.** Malformed input yields a partial result plus
   `ParsedMessage.warnings: Vec<String>`. Only truly unparsable input (empty bytes, no
   headers at all) returns `Err`.
5. **Owned, lifetime-free public types.** `ParsedPart` and `ParsedMessage` carry no
   lifetime parameters. Both implement `Serialize + Deserialize`.
6. **Byte ranges are indices, not slices.** `(u32, u32)` — offset and length into the
   caller's `&[u8]`. The crate never stores the raw bytes internally.
7. **`decode_body_value` is the only decode path.** Transfer-encoding decode and charset
   conversion happen on demand, not at parse time.

## Architecture

```
parse(raw: &[u8]) -> Result<ParsedMessage, ParseError>
                               │
              ┌────────────────┴─────────────────┐
              │           ParsedMessage           │
              │  part_index: ParsedPart (tree)    │
              │  text_body:  Vec<String> (ids)    │
              │  html_body:  Vec<String> (ids)    │
              │  attachments: Vec<String> (ids)   │
              │  headers:    Vec<ParsedHeader>    │
              │  preview:    Option<String>       │
              │  warnings:   Vec<String>          │
              └───────────────────────────────────┘

decode_body_value(raw: &[u8], part: &ParsedPart, max_bytes: Option<usize>)
    -> Result<DecodedBodyValue, ParseError>
```

`ParsedPart` fields:
- `part_id: String` — IMAP dotted path
- `content_type: String`, `charset: Option<String>`
- `transfer_encoding: TransferEncoding` — `Identity | QuotedPrintable | Base64 | SevenBit | EightBit | Binary`
- `disposition: Option<String>`, `filename: Option<String>`, `cid: Option<String>`
- `header_range: (u32, u32)` — offset/len of part headers in raw bytes
- `body_range: (u32, u32)` — offset/len of body before transfer-decode
- `children: Vec<ParsedPart>` — non-empty only for `multipart/*`

## Dependencies

| Crate | Role | Leaks into public API? |
|---|---|---|
| `mail-parser` | RFC 5322/MIME parsing engine | No |
| `encoding_rs` | Charset conversion | No |
| `serde` | `Serialize + Deserialize` derives | Yes |

No async deps. No tokio. Synchronous only.

## RFC References

| RFC | Covers |
|---|---|
| RFC 5322 | Internet Message Format |
| RFC 2045 | MIME — Format of Internet Message Bodies |
| RFC 2046 | MIME — Media Types (multipart boundaries) |
| RFC 2047 | MIME — Encoded-Word in headers |
| RFC 2183 | Content-Disposition |
| RFC 8621 §4.1.4 | Body structure algorithm (textBody / htmlBody / attachments) |

All RFC text files live in `~/PROJECT/MIME/standards/` — read from there.
See `~/PROJECT/MIME/standards/README.md` for the full index.

## Conventions

- License: MIT OR Apache-2.0
- MSRV: 1.85
- No `unsafe`
- Error types: defined in `error.rs`, exported from crate root

## Test Integrity

- Never modify, skip, or weaken a failing test. Fix the code.
- Oracles: real RFC 5322 messages; cross-validate decoded output against Python's
  `email.parser` module or `mailparse` CLI. Never derive expected values from this crate.
- RFC 8621 §4.1.4 body structure tests: hand-crafted MIME messages with known expected
  structure, not messages generated by this crate.

## Workspace Context

This crate lives in `~/PROJECT/MIME/mime-tree/`.
Workspace CLAUDE.md: `~/PROJECT/MIME/CLAUDE.md`.
Sibling crate: `smime-tree` (depends on this crate; this crate does not depend on it).
JMAP adapter: `~/PROJECT/JMAP/crate-jmap-mime/` (not part of this workspace).
