Skip to content

pimalaya/mml

Repository files navigation

📝 MML Documentation Matrix Mastodon

CLI and lib for the Emacs MIME message Meta Language (MML), written in Rust.

This repository ships two layers:

  • Low-level library exposing two pipelines (MML→MIME compiler, MIME→MML interpreter) and a template builder for compose/reply/forward drafts.
  • High-level CLI wrapping the library, plus editor-driven compose / reply / forward commands bundling "template → $EDITOR → compile → validate/re-edit/view/abort", and interpret (aliased read) for the inverse MIME→MML flow.

Table of contents

Features

  • MML → MIME compilation (requires compiler feature):
    • <#part> / <#multipart> directives with type, filename, disposition, encoding, description, name, recipient-filename, dates, etc.
    • Inline parts, attached parts, nested multiparts (alternative, mixed, related)
    • File-path expansion via shellexpand
    • MIME-type detection via tree_magic_mini
    • Parse-error reporting via ariadne (CLI)
  • MIME → MML interpretation (requires interpreter feature):
    • Header include / exclude filters
    • Part include / exclude filters
    • HTML → text rendering via nanohtml2text
    • Attachment save-to-disk
    • mml interpret (aliased mml read): MIME on stdin, MML/text on stdout (himalaya read-with slot)
  • Editor-driven flow (requires cli + compiler + interpreter):
    • mml compose / mml reply / mml forward: open $EDITOR, compile on save, prompt to validate / re-edit / view / abort
  • TOML configuration with per-account identities and per-section defaults ([compose], [reply], [forward], [read])

Tip

MML is written in Rust and uses cargo features to gate functionality. The default feature set is declared in Cargo.toml.

Installation

Pre-built binary

The CLI binary mml can be installed from the latest GitHub release using the install script:

As root:

curl -sSL https://raw.githubusercontent.com/pimalaya/mml/master/install.sh | sudo sh

As a regular user:

curl -sSL https://raw.githubusercontent.com/pimalaya/mml/master/install.sh | PREFIX=~/.local sh

For a more up-to-date version, check out the pre-releases GitHub workflow: pick the latest run and grab the artifact matching your OS. These are built from the master branch.

Note

Pre-built binaries are built with the default cargo features. If you need a different feature set, use another installation method.

Cargo

cargo install mml --locked

You can also use the git repository for a more up-to-date (but less stable) version:

cargo install --locked --git https://github.com/pimalaya/mml.git

To use mml as a library, add it to your Cargo.toml:

[dependencies]
mml = { version = "1.0", default-features = false, features = ["compiler", "interpreter"] }

Drop cli (and pick only compiler and/or interpreter) for a slim library build with no clap, no ariadne, no editor integration.

Nix

If you have the Flakes feature enabled:

nix profile install github:pimalaya/mml

Or run without installing:

nix run github:pimalaya/mml -- compile <<<'<#part>Hello, world!<#/part>'

Sources

git clone https://github.com/pimalaya/mml
cd mml
nix run

Configuration

A sample config.sample.toml is shipped at the repository root. Drop it into one of:

  • $XDG_CONFIG_HOME/mml/config.toml
  • $HOME/.config/mml/config.toml
  • $HOME/.mmlrc

Override the path with -c <PATH> or MML_CONFIG=<PATH>.

CLI flags always win; config values fill in the blanks. Pick an account with -a <NAME>, or flag one entry default = true.

Usage

Library

Compile MML to MIME:

use mml::compiler::message::MmlCompilerBuilder;

let mml = "<#part>Hello, world!<#/part>";
let mime = MmlCompilerBuilder::new()
    .build(mml)?
    .compile()?
    .into_string()?;

println!("{mime}");

Interpret MIME back to MML:

use mml::interpreter::message::MimeInterpreterBuilder;

let mime = b"From: a@b\r\nTo: c@d\r\nSubject: Hi\r\n\r\nHello!\r\n";
let mml = MimeInterpreterBuilder::new()
    .with_show_only_headers(["From", "To", "Subject"])
    .build()
    .from_bytes(mime)?;

println!("{mml}");

CLI

Compile MML on stdin, emit MIME on stdout:

mml compile <<< '<#part>Hello, world!<#/part>'

Interpret MIME back to MML/text:

mml interpret < message.eml

Open the editor on a fresh compose draft, then emit the compiled MIME message on stdout:

mml compose --from me@example.org

Reply / forward from a piped MIME message:

cat message.eml | mml reply --all
cat message.eml | mml forward

Read (MIME → text) for himalaya's read-with:

cat message.eml | mml read --exclude-header Received,DKIM-Signature

Generate a draft template without opening the editor:

mml template compose --from me@example.org
mml template reply --all < message.eml
mml template forward < message.eml

Plug mml into himalaya v2:

[message.composer.mml]
command = "mml compose"
default = true

[message.reader.mml]
command = "mml read"
default = true

FAQ

How to debug the CLI?

Use --log <level> where <level> is one of off, error, warn, info, debug, trace:

mml --log trace compile < message.mml

The RUST_LOG environment variable, when set, overrides --log and supports per-target filters (see the env_logger documentation). RUST_BACKTRACE=1 enables full error backtraces, including source lines where the error originated from.

Logs are written to stderr, so they can be redirected easily to a file:

mml --log trace compile < message.mml 2>/tmp/mml.log
How does `mml compose` pick the editor?

The edit crate resolves $VISUAL first, then $EDITOR, then an OS default. mml does not expose a config knob on top: set VISUAL / EDITOR in your shell rc file.

License

This project is licensed under either of:

at your option.

AI disclosure

This project is developed with AI assistance. This section documents how, so users and downstream packagers can make informed decisions.

  • Tools: Claude Code (Anthropic), Opus 4.7, invoked locally with a persistent project-scoped memory and a small set of repo-specific rules.

  • Used for: Refactors, mechanical multi-file edits, boilerplate (feature gates, error enums, derive macros, trait impls), test scaffolding, doc polish, exploratory design conversations.

  • Not used for: Engineering, critical code, git manipulation (commit, merge, rebase…), real-world tests.

  • Verification: Every AI-assisted change is read, compiled, tested, and formatted before commit (nix develop --command cargo check / cargo test / cargo fmt). Behavioural correctness is verified against the relevant RFC or upstream spec, not assumed from the model output. Tests are never adjusted to fit AI-generated code; the code is adjusted to fit correct behaviour.

  • Limitations: AI models occasionally produce code that compiles and passes tests but is subtly wrong: off-by-one errors, missed edge cases, plausible but nonexistent APIs, stale RFC references. The verification workflow catches most of this; it does not catch all of it. Bug reports are welcome and taken seriously.

  • Last reviewed: 31/05/2026

Social

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:

If you appreciate the project, feel free to donate using one of the following providers:

GitHub Ko-fi Buy Me a Coffee Liberapay thanks.dev PayPal