simple_endian 0.4.3

A create for defining endianness within your data structures, to make handling portable data structures simpler.
Documentation
# simple_endian — LLM usage notes

This file is meant for LLM-powered tooling (code assistants, agentic refactoring tools, doc bots) to quickly learn how to use this repository’s Rust crates correctly.

## What this crate is

`simple_endian` helps you **describe binary formats in Rust** (network protocols, file formats, on-disk/on-wire structs) with endianness made explicit in types.

It provides:

- Endian-aware wrappers: `BigEndian<T>`, `LittleEndian<T>`
- Shorthand aliases like `u16be`, `u32le`, etc.
- Safe endian-aware IO helpers: `read_specific` / `write_specific` (feature-gated)
- A derive macro: `#[derive(Endianize)]` (feature-gated) that generates `*Wire` types.

## Feature gates you must respect

Most functionality is behind cargo features:

- `derive`: enables `#[derive(Endianize)]` **and re-exports it** as `simple_endian::Endianize`
- `io-std`: enables std IO traits and exports `read_specific` / `write_specific` for `std::io::{Read, Write}`
- `io-core`: enables a core-IO-compatible API (no `std`)
- `text_utf8`, `text_utf16`, `text_utf32`, `text_fixed`, `text_all`: fixed-size text field helpers

When generating code, include the right feature flags in examples/tests so it compiles.

## Core modeling pattern (recommended)

Prefer “logical type” + generated “wire type”:

- Logical types use native Rust primitives (`u16`, `u32`, `String`, etc.) and are used by application logic.
- Wire types are generated `*Wire` types where fields become endian-stable (`BigEndian<T>`/`LittleEndian<T>` etc.).

This keeps endian conversions explicit at boundaries.

## IO pattern (preferred)

Use the crate IO helpers whenever possible, rather than manual byte swapping:

- Read a typed wire value:
  - `let x: u32be = read_specific(reader)?;`
- Write a typed wire value:
  - `write_specific(writer, &x)?;`

This is the preferred approach in examples in this repo.

## The Endianize derive macro

### Basic usage

```rust
use simple_endian::Endianize;

#[derive(Endianize)]
#[endian(be)]
struct Header {
    a: u32,
    b: u16,
}

// Generates: HeaderWire { a: BigEndian<u32>, b: BigEndian<u16> }
```

- `#[endian(be)]` or `#[endian(le)]` is required on the container.

### Fixed-size text fields

On **named struct fields only**, you can generate fixed-size padded wire text fields:

```rust
#[derive(Endianize)]
#[endian(le)]
struct Msg {
    #[text(utf16, units = 8, pad = "space")]
    title: String,
}
```

This affects the generated `MsgWire` field type (it becomes a fixed UTF wrapper type).

### Enum support

`Endianize` supports enums as a **tag + payload** wire type.

Requirements:

- The enum must declare `#[repr(u8|u16|u32|u64)]`.
- Only unit variants and **named-field** variants are supported.
- If any variant carries payload, all variants must have explicit discriminants.

### Union support

`Endianize` generates a `*Wire` union (fields become endian-wrapped), but **does not** generate IO impls for unions.

### Wire layout control (padding/alignment): `#[wire_repr(...)]`

By default generated wire types use `#[repr(C)]`, which may introduce padding.

You can override the representation used on generated wire types:

```rust
#[derive(Endianize)]
#[endian(be)]
#[wire_repr(packed)]
struct PackedHeader {
    a: u8,
    b: u32,
    c: u16,
}
```

Safety notes for LLMs:

- `#[repr(packed)]` means fields may be unaligned.
- Rust forbids taking references to packed fields (E0793).
- The derive macro in this repo was updated to avoid taking references to packed fields in generated impls.
- In user-written code, prefer IO routines and/or `read_unaligned` into temporaries rather than `&wire.field`.

### Wire trait passthrough: `#[wire_derive(...)]`

To put extra derives on generated `*Wire` containers:

```rust
#[derive(Endianize)]
#[endian(le)]
#[wire_derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Msg {
    id: u16,
    len: u32,
}

// MsgWire will derive those traits.
```

## Repo-specific conventions

- Examples prefer `read_specific`/`write_specific` over manual endian conversions.
- Tests frequently run with `cargo test --all-features`.
- Keep new derive features fully feature-gated and ensure compilation both with and without `derive`.

## Common pitfalls

- Don’t assume `repr(C)` means “no padding”. It doesn’t.
- Don’t take references to packed fields.
- If you add new derive-generated code, ensure it compiles for packed wire types.

## Where to look in this repo

- Derive macro crate: `simple_endian_derive/`
  - Entry: `simple_endian_derive/src/lib.rs`
  - Implementation: `simple_endian_derive/src/endianize.rs`
- IO: `src/io.rs`
- Endian wrappers: `src/specific_endian.rs` and shorthand types in `src/shorthand_types.rs`