MEP-73 Phase 9: TargetRustLibrary emit
Status: LANDED (2026-05-29)
Spec section: MEP-73 §3 Direction 2 — Rust library emit
Worktree: /Users/apple/mochi-mep73-p9
Gate
Land the publish-direction emit pipeline: lower a Mochi package's
public surface (the things its pub fn / pub struct / pub enum
declarations expose) into a publishable Rust library crate whose
Cargo.toml declares crate-type = ["rlib", "cdylib"] and which
optionally ships a cbindgen-compatible C header so non-Rust consumers
can link against the cdylib.
Why it matters
Phases 0-8 owned the consume direction (import rust brings a crates.io
crate into a Mochi program). Phase 9 opens the producer direction:
mochi pkg publish --to=crates.io ships a Mochi package outward as a
first-class Rust crate. Without this gate, Mochi code is a consumer of
the Rust ecosystem but not a contributor to it.
The lowered crate has three artefacts:
Cargo.toml—[package]metadata +[lib]configuration pinning bothrlib(so downstream Cargo users seeextern crate <mochi_crate>;semantics) andcdylib(so the same artefact links into C / C++ / Mochi / Python via FFI).src/lib.rs— public Rust source rendered from the Mochi package's public surface.pub fnitems lower topub fn(or#[no_mangle] pub extern "C" fnwhen the surface is exported with the C ABI).pub struct/pub enumitems lower topub struct/pub enumwith optional#[repr(C)]and#[derive(...)]attributes.include/<crate>.h— optional cbindgen-style header listing every repr(C) type and everyextern "C"function declaration, wrapped in the standard#ifdef __cplusplusextern-C block. Only emitted whenCHeader = true.
The emit pipeline is byte-stable: given the same PublicAPI input,
Render returns byte-identical files. This is the contract the
publish flow (Phase 10) needs to produce reproducible source tarballs
that Sigstore-keyless OIDC trusted-publishing signs.
What landed
package3/rust/library/library.go
The core types and Render entry point.
PublicAPI{CrateName, Version, Items, Package, Dependencies, CHeader, NoStd}— closed input shape callers construct from their IR state.PackageMeta{Description, License, Repository, Documentation, Homepage, Keywords, Categories, Authors, Readme, Edition, RustVersion}— the publish-side metadata block matching crates.io's schema.Iteminterface (sealed) with three concrete types:ItemFn{Name, Params, Return, Body, Extern, Doc}ItemStruct{Name, Fields, ReprC, Derives, Doc}ItemEnum{Name, Variants, ReprC, Derives, Doc}
Render(api PublicAPI) (Files, error)— validates structure (crate-name shape, duplicate item names, body presence on non-extern fns) and dispatches to the three renderers.validCrateNameenforces the crates.io naming rule: ASCII letter start, followed by letters / digits /_/-.
package3/rust/library/manifest.go
The Cargo.toml renderer.
- Byte-stable hand-rolled TOML (no third-party dep) matching the same
style as Phase 7's
workspace.go. [package]rows in fixed order: name, version, edition, rust-version, description, license, repository, documentation, homepage, readme, authors, keywords, categories.[lib]block always renderscrate-type = ["rlib", "cdylib"].[dependencies]block sorts by crate name; rows render asname = "version-req".- Empty metadata fields are omitted (no
key = ""rows). - Default edition is
2021.
package3/rust/library/lib_rs.go
The src/lib.rs renderer.
- Optional
#![no_std]preamble +extern crate alloc;whenNoStd = true. - One Rust item per
Itemin declaration order, separated by blank lines. ItemFnrenders topub fn(default) or#[no_mangle] pub extern "C" fn(whenExtern). Unit return()is elided in the signature.ItemStructrenders#[repr(C)]/#[derive(...)]attributes when set, followed bypub struct Name { ... fields ... }. Field-levelPub boolcontrols per-field visibility.ItemEnumrenders attributes followed bypub enum Name { ... }; unit variants render as bare names, struct-style variants render asName { field: Type, ... }.///doc-comments render preceding the item / field / variant.
package3/rust/library/cheader.go
The cbindgen-compatible C header renderer.
- Standard layout:
#ifndef <CRATE>_H/#define/#include <stdint.h>/#include <stddef.h>/extern "C" {wrapper / body / closing. - Only
Externfunctions andReprCtypes are emitted; non-extern fns and non-repr-C types are silently skipped (they are not ABI-stable, so they don't belong in a C header). rustTypeToCprojects the closed set of repr(C)-safe Rust types into canonical C spellings:i64→int64_t,f64→double,*const c_char→const char*,*mut T→<T_in_C>*, and so on. Unknown types render verbatim (escape hatch for opaque-handle typedef names callers register at the Rust level).- Tagged-union enums (variants with payloads) render a deferral comment pointing at Phase 12 — they require the monomorphisation-style tagged-union projection cbindgen produces.
Tests
library_test.go— 14 cases covering Render shape, validation, determinism, Files.Sorted, RenderError formatting, name helpers.manifest_test.go— 7 cases covering basic shape, custom edition, empty-metadata omission, full metadata, dep sorting, omitted[dependencies]section,[lib]block layout.lib_rs_test.go— 11 cases covering plain fn, extern fn, void return elision, struct render, unit-only and struct-variant enums, no_std preamble, doc comments, multi-item separation, body trimming, private fields.cheader_test.go— 13 cases covering include guard, stdint includes, extern-C wrap, struct / enum / fn renderers, skipping non-emit items, void / void params, the rustTypeToC table, and hyphenated crate-name handling.phase09_test.go(sentinel) with subtests:publishable_crate_shape: full Cargo.toml + lib.rs + header end-to-end for a Point-distance example.determinism: two Render calls produce byte-identical output.nostd_subset:NoStd = trueemits#![no_std]+extern crate alloc.c_header_omitted_by_default:CHeader = falseproduces noinclude/...file.
Target matrix
| Target | Status | Notes |
|---|---|---|
| Schema | ✅ | PublicAPI / PackageMeta / Item closed shapes. |
| Cargo.toml | ✅ | Deterministic, crate-type = ["rlib", "cdylib"]. |
| src/lib.rs | ✅ | pub fn / pub struct / pub enum + #[repr(C)] + #[derive]. |
| Extern "C" fns | ✅ | #[no_mangle] pub extern "C" fn + matching C decl. |
| cbindgen header | ✅ | Include guard + stdint + extern-C wrap; unit-enum + repr(C) struct supported. |
| no_std subset | ✅ | #![no_std] + extern crate alloc; preamble. |
How this phase plugs in to the larger pipeline
Mochi public surface Phase 9 emit
(caller's IR)
┌─────────────────────────┐
pub fn ... ───┐ │ library.PublicAPI │
pub struct ... │ caller projects via │ CrateName, Version, │
pub enum ... │ their IR walker │ Items[...], │
pub type ... │ │ Package{...}, │
│ │ Dependencies{...} │
▼ └────────────┬────────────┘
│
library.Render(api)
│
┌────────────────┴─────────────── ─┐
▼ ▼
Cargo.toml src/lib.rs
[lib] crate-type= pub fn / struct /
["rlib","cdylib"] enum (+repr(C))
│
optional: include/<crate>.h
The next phase (10. trusted publishing) consumes this output: writes
the rendered files to a source tarball, runs cargo package, and
ships through Sigstore-keyless OIDC to crates.io. Phase 9 is the
upstream half of that pipeline; the contract between phase 9 and
phase 10 is exactly the Files map Render returns.
Timeline
| Time (GMT+7) | Step |
|---|---|
| 2026-05-29 23:35 | Worktree branch mep/0073-phase-09 created off origin/main. |
| 2026-05-29 23:48 | library.go, manifest.go, lib_rs.go, cheader.go written. |
| 2026-05-29 23:50 | Test suite green (go test ./package3/rust/...). |
| 2026-05-29 23:53 | Tracking page + spec sync. |