mdict-rs 0.1.4

Library-first Rust parser for MDict .mdx and .mdd dictionaries
Documentation
# mdict-rs

`mdict-rs` is a library-first Rust parser for MDict `.mdx` and `.mdd`
dictionaries.

The project currently targets the widely used v2.0 layout with a strong bias
toward:

- checked parsing over panics
- lazy key / record block decoding
- library APIs before CLI tooling
- clean-room interoperability instead of GPL code translation

## Status

`mdict-rs` is ready for an early public release in the `0.1.x` range.

What the current release means:

- the v2.0 parser path is implemented and tested against a local corpus of real
  dictionaries
- the public API is intentionally small
- format coverage is still conservative rather than “all historical MDict
  variants”

## Supported Scope

Currently supported:

- MDX v2.0
- MDD v2.0
- lazy lookup, key iteration, ordinal key access, and entry iteration
- encrypted keyword-index dictionaries
- passcode-aware keyword-header support in the API
- block compression:
  - uncompressed
  - zlib
  - optional `lzo` feature
- encodings:
  - UTF-8
  - UTF-16LE
  - GBK / GB18030-family
  - Big5

Currently not supported:

- version 1.x section layouts
- writer support
- HTML / stylesheet rewriting
- mmap or sidecar indexes

## Installation

```toml
[dependencies]
mdict-rs = "0.1"
```

If you need LZO-compressed blocks:

```toml
[dependencies]
mdict-rs = { version = "0.1", features = ["lzo"] }
```

## Quick Start

### MDX lookup

```rust,no_run
use mdict_rs::MdxFile;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dict = MdxFile::open("dictionary.mdx")?;
    if let Some(record) = dict.lookup("apple")? {
        println!("{}", record.text);
    }
    Ok(())
}
```

### MDD resource lookup

```rust,no_run
use mdict_rs::MddFile;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dict = MddFile::open("dictionary.mdd")?;
    if let Some(resource) = dict.lookup("\\\\LM5style.css")? {
        println!("{} bytes", resource.data.len());
    }
    Ok(())
}
```

### Key-only iteration

```rust,no_run
use mdict_rs::MdxFile;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dict = MdxFile::open("dictionary.mdx")?;
    for key in dict.keys().take(10) {
        println!("{}", key?);
    }
    Ok(())
}
```

Use `entries()` when you also need the decoded MDX text or MDD payload for each
item.

### Stable key ordinals

```rust,no_run
use mdict_rs::{KeyOrdinal, MdxFile};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dict = MdxFile::open("dictionary.mdx")?;
    for entry in dict.keys_with_ordinals().take(3) {
        let entry = entry?;
        println!("{}: {}", entry.ordinal.get(), entry.key);
    }

    let key = dict.key_at(KeyOrdinal::from(42))?;
    println!("{key:?}");
    Ok(())
}
```

### Batched ordinal resolution

```rust,no_run
use mdict_rs::{KeyOrdinal, MdxFile};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dict = MdxFile::open("dictionary.mdx")?;
    let keys = dict.keys_at(&[
        KeyOrdinal::from(7),
        KeyOrdinal::from(42),
        KeyOrdinal::from(7),
    ])?;
    println!("{keys:?}");
    Ok(())
}
```

## Validation

The current codebase is verified with:

- unit tests for crypto, compression, and header parsing
- malformed-input tests
- mutation-style regression tests
- ignored integration tests against a large local sample
- ignored integration tests against the full local dictionary corpus
- a separate `fuzz/` crate with libFuzzer entrypoints

## License

This repository is publicly available under `AGPL-3.0-only`.

Commercial licensing is also available from the copyright holder for users who
want to embed `mdict-rs` without AGPL obligations.

See [COMMERCIAL-LICENSING.md](COMMERCIAL-LICENSING.md).