What "faithful" means

A faithful port mirrors the upstream's structure. Same function names, same enum variants in the same order, same match/switch arms in the same order, same error message strings. The Rust file and the upstream file can be opened side by side and verified line by line.

Upstream (C)
switch (op->kind) { case OP_ADD: return add(a, b); case OP_SUB: return sub(a, b); case OP_MUL: return mul(a, b); default: return err("bad op"); }
Port (Rust)
match op.kind { Op::Add => add(a, b), Op::Sub => sub(a, b), Op::Mul => mul(a, b), _ => Err("bad op".into()), }

That's mundane on this example, and that's the point: nothing fancy, no extracted helper, no consolidated branches. The same shape end to end.

Why structural fidelity matters

Behavior can be the same for the wrong reasons — a bug that cancels another bug, a "cleaner" rewrite that happens to pass the tests you wrote and breaks on the ones you didn't. When the port's structure matches the upstream's, you can verify equivalence locally, function by function. That cross-check catches drift that no unit test would notice.

The other payoff is upstream tracking. The upstream keeps moving — bug fixes, security patches, new features. When the port mirrors the upstream's layout, a future change is a localised translation: open the upstream diff, find the matching block in the port, translate it. The port doesn't gradually become an abandoned fork.

What rustports is not

rustports isn't:

The shape of the workflow

Every port follows the same three-step workflow per slice of code:

  1. Skeleton — types and function signatures with todo!() bodies. cargo check green. The structural mirror exists before any body is written.
  2. Filltodo!() bodies become faithful translations of the upstream, one function at a time. No refactoring; no "clean up while we're here."
  3. Debug — plug the module into the runtime, run the differential test harness, fix the diffs. The harness is always the oracle.

The full methodology — phase planning, idiom maps per source language, deferral discipline, the catalogue of anti-patterns — lives in rustports/docs/methodology.md. It applies regardless of the upstream's language: every current port (Haskell, Perl, C, C++, Java) uses the same playbook.

When to reach for rustports

Use a rustports crate when you need the tool's full behaviour from inside a Rust program, and want it without a subprocess fence or an FFI build dependency.

If you only need a sliver of behaviour, a small purpose-built crate may serve you better. If you need to ship and you're fine with Command::new(...), shipping the upstream binary is fine too. rustports earns its weight when the upstream's full feature set, in-process, is what you actually want.