littlefs2-rust 0.1.1

Pure Rust littlefs implementation with a mounted block-device API
Documentation
# Configuration and Device Contract

This document describes the public configuration surface for release users.
Longer implementation notes remain in `docs/architecture.md`; this file is the
short operational contract.

## Features

The default feature set enables `std` and exports `FileBlockDevice`.

```toml
[dependencies]
littlefs-rust = { package = "littlefs2-rust", version = "0.1" }
```

For `no_std` targets, disable default features. The library still depends on
`alloc`, so the final firmware must provide a global allocator.

```toml
[dependencies]
littlefs-rust = { package = "littlefs2-rust", version = "0.1", default-features = false }
```

## Physical Geometry

`Config` contains only the physical image geometry:

```rust
use littlefs_rust::Config;

let cfg = Config {
    block_size: 512,
    block_count: 4096,
};
```

The block size and block count must match the image being mounted. The
superblock is still parsed and checked after mount.

## Filesystem Options

`FilesystemOptions` mirrors the main non-callback fields of C littlefs'
`struct lfs_config`. The simple APIs use `FilesystemOptions::default()`;
the `*_with_options` APIs accept caller-provided values:

- `Filesystem::format_device_with_options`
- `Filesystem::mount_device_with_options`
- `Filesystem::mount_device_mut_with_options`
- `Filesystem::mount_with_options`

Supported fields:

| Field | Meaning |
| --- | --- |
| `read_size` | Minimum aligned read unit used for config validation and cache geometry. |
| `prog_size` | Minimum aligned program unit used by metadata commits and config validation. |
| `cache_size` | Parser/cache chunk target; clamped to the block size where appropriate. |
| `lookahead_size` | Accepted and validated to match C config shape. The current allocator still uses an in-memory bitmap plus a fixed internal scan window. |
| `block_cycles` | Metadata relocation cadence. `None` disables block-cycle relocation. |
| `name_max` | Maximum file or directory name length recorded in the superblock. |
| `file_max` | Maximum file size recorded in the superblock and checked by write paths. |
| `attr_max` | Maximum user attribute size recorded in the superblock and checked by attr writes. |
| `metadata_max` | Optional metadata-pair working limit used for validation and inline threshold derivation. |
| `inline_max` | Inline-file policy, represented by `InlineMax`. |

Example:

```rust
use littlefs_rust::{FilesystemOptions, InlineMax};

let options = FilesystemOptions {
    read_size: 16,
    prog_size: 16,
    cache_size: 128,
    block_cycles: Some(256),
    metadata_max: Some(512),
    inline_max: InlineMax::Limit(64),
    ..FilesystemOptions::default()
};
```

Validation follows the littlefs relationships that are visible through the
public API:

- `read_size`, `prog_size`, `cache_size`, and `lookahead_size` must be nonzero.
- `cache_size` must be divisible by `read_size` and `prog_size`.
- `block_size` must be divisible by the effective cache size.
- `prog_size` must be a power of two.
- `lookahead_size` must be byte-aligned to a multiple of 8.
- `block_cycles = Some(0)` is rejected.
- `name_max`, `file_max`, and `attr_max` must be nonzero and inside the
  littlefs-compatible maxima.
- `metadata_max`, when present, must divide the block geometry and align to
  read/program sizes.

## Inline Policy

`InlineMax` avoids exposing C's sentinel integer values directly:

| Rust value | Meaning |
| --- | --- |
| `InlineMax::Default` | Derive C's default inline threshold from cache, attr, and metadata limits. |
| `InlineMax::Disabled` | Disable non-empty inline file data. |
| `InlineMax::Limit(n)` | Use an explicit inline threshold after validation. |

## Block Device Contract

Implementors provide four operations:

```rust
use littlefs_rust::{BlockDevice, Config, Result};

struct MyFlash;

impl BlockDevice for MyFlash {
    fn config(&self) -> Config {
        Config {
            block_size: 512,
            block_count: 4096,
        }
    }

    fn read(&self, block: u32, off: usize, out: &mut [u8]) -> Result<()> {
        todo!()
    }

    fn prog(&mut self, block: u32, off: usize, data: &[u8]) -> Result<()> {
        todo!()
    }

    fn erase(&mut self, block: u32) -> Result<()> {
        todo!()
    }

    fn sync(&mut self) -> Result<()> {
        Ok(())
    }
}
```

Rules:

- `read` and `prog` operate within one logical block. Cross-block requests are
  invalid.
- `prog` should model flash programming: bits can move from `1` to `0` without
  erase, but not from `0` back to `1`.
- `erase` resets one whole block to the erased state expected by littlefs,
  normally `0xff`.
- `sync` should flush backend state when the platform has a separate durable
  boundary. It may be a no-op for synchronous memory-mapped or test devices.

`MemoryBlockDevice` and `FileBlockDevice` both follow these rules and are used
by integration tests as real backends.

## Full-Image Utilities

`Filesystem::mount(&[u8])`, `ImageBuilder`, and `ImageEditor` are useful for
fixtures, corruption tests, and host tools that already own a complete image.
Mounted production-style code should prefer `mount_device` or
`mount_device_mut`, because those paths avoid resident capacity-sized image
ownership.