- name
- rust-java-migration
- license
- Apache-2.0
- description
- Plan, execute, audit, and verify behavior-preserving migrations from Java Maven or Gradle projects to project-shaped Rust Cargo workspaces, including derived crate boundaries, scale-appropriate topology, 100% lossless source-test/case migration, byte-identical test assets, and complete per-case differential parity. Use when comparing repositories at module, package, object, file, method, parameter, documentation, example, test, fixture/data, dependency-reuse, JavaBean/script-property, concurrency, runtime-behavior, oversized Rust-file, or test-organization level; producing migration documents; continuing an incomplete port; or repairing workspace drift. Enforces source-authoritative inventories, Rust-native APIs, a 500-line cohesion-review threshold and 800-line authored-file blocker, idiomatic unit/integration test placement, strict non-completion states, frozen baselines, and unified verification.
# Java to Rust Migration
Migrate contracts and observable behavior, not Java syntax. Preserve the Java project's public concepts and source traceability while selecting Rust-native ownership, error, concurrency, async, serialization, and framework mechanisms.
## Mandatory migration layout
- Derive the Rust directory from the Java package after the declared package root; preserve meaningful nested packages and never flatten, cross-place, or double-nest them.
- Convert the Java object name to acronym-aware `snake_case.rs`; keep the Rust type in `PascalCase` and do not add suffixes such as `_trait`.
- Keep one Java object as one Rust object boundary. Rust defines no universal file-length limit: treat up to 500 physical lines as normal, review 501–800 lines for cohesion, and block authored `.rs` files above 800 lines.
- Split only when a file mixes responsibilities. Retain one primary object file and use subordinate files for coherent implementation families without introducing a second migrated object; record every split or reviewed exception.
- Keep `lib.rs` and `mod.rs` to declarations and re-exports. Update parent modules after moves and qualify ambiguous external re-exports with `::`.
- Keep focused private-behavior unit tests in `#[cfg(test)]` modules when useful. Put public-boundary, cross-module, differential, host, and whole-project tests under `tests/` or a non-published `<project>-test` package; keep source fixtures under `<project>-test/tests/fixtures/`.
Read [Directory path alignment](references/directory-path-alignment.md) and [Layout and governance](references/layout-and-governance.md), then run the layout audit after moves.
## Scope and Routing
Use this skill for full-project migrations, one Maven/Gradle module, parity audits, migration planning, or continuation of an existing Rust port.
This is the Rust adapter of the [cross-language migration profile](references/cross-language-migration-profile.md). Route crate/layout/API, dependency, macro, and domain mechanics to the corresponding Rust skills. Route verification to `rust-java-migration-testing` plus relevant test skills.
Do not modify migration code when the user requested only an audit, plan, or documentation. A plan-only or read-only request does not authorize running a write-producing document scaffolder: return the proposed four-document content in the response, use `--dry-run`, or write only to a user-approved destination. Do not broaden a module migration into a repository rewrite without authorization.
## Required Inputs
Resolve or explicitly mark unknown:
- Java repository path, baseline commit/tag, build tool, JDK, and module scope.
- Exact Java module package root used for path mapping; do not pass only a repository or `src/main/java` root and guess which segments to strip.
- Rust repository path, baseline commit, toolchain/MSRV, workspace, and target platforms.
- Target-product topology inputs: independently published Rust units, dependency/feature/target/proc-macro/FFI boundaries, expected crate count, package families, root noise, other languages, and established paths.
- Compatibility goal: source-shape parity, public API parity, behavior parity, or production replacement.
- Dependency policy: license, MSRV, supported targets, unsafe policy, advisory policy, maintenance horizon, and acceptable transitive cost.
- Component-candidate sources and their observation date; distinguish team policy, researched candidates, declared dependencies, and verified adoption.
- Explicit exceptions, blocked external projects, unsupported JVM-only features, and completion deadline.
- Required host applications, real scripts, test data, concurrency model, load profile, and rollback mechanism.
- Existing migration documents and their authority; identify the single current four-document set before merging historical material.
- Complete source test roots/runner configuration, including every concrete test case, disabled test, fixture, script, corpus, golden file, resource, and data file. The source repository defines the denominator; do not narrow it to fit Rust.
Never silently infer that the newest branch, a generated manifest, or an API registration list is the behavioral baseline.
## Workflow
Treat one declared Java source module and its Rust target crate/module as the default migration batch. A user-authorized multi-module scope may be one batch, but freeze its boundary before implementation. Apply: freeze scope/contracts → implement the batch → freeze → audit → unified verification.
Dependency-ordered editing inside the implementation batch is allowed. Per-object completion loops are not.
### 1. Freeze baselines and inspect repository state
Record both repository SHAs, dirty worktrees, Java/Rust toolchains, module manifests, enabled features, and generated-code boundaries. Preserve existing Rust work and unrelated changes.
Verify the Rust toolchain satisfies the workspace MSRV: an older default rustc fails with `rustc X is not supported by the following packages` — run gates via `rustup run <ver> cargo ...`, never silently lower `rust-version`.
If a repository contains `.codegraph/`, use CodeGraph before text search or file-by-file reading:
1. Survey module/package/crate architecture.
2. Query representative public types and overloaded methods.
3. Trace high-value call chains across factories, registries, interceptors, serializers, persistence, networking, and concurrency.
4. Query the Rust counterparts and their callers/tests.
5. Refresh or re-query the module inventory before implementation if the index reports staleness.
If no index exists, do not initialize one without authorization. Use language-aware tooling or targeted source inspection and disclose the weaker evidence.
Read [CodeGraph parity audit](references/codegraph-parity-audit.md) for query patterns and inventory rules.
Read [Case-study lessons](references/case-study-lessons.md) when designing a large utility-library migration or an annotation/macro split.
### 1.5. Derive Rust crate boundaries and workspace topology
Do not map Maven/Gradle modules one-to-one to Cargo packages. Derive Rust packages from independent publishing/reuse, dependency/feature/target isolation, proc-macro or FFI constraints, and distinct lifecycles; source modules remain traceability scopes.
Select placement with `rust-workspace`: small cohesive results are normally root-flat; real adapter/binding/example/test families support a hybrid; large, root-heavy, or multi-language repositories may use `crates/`. Counts are review triggers, not laws. Record the source-module-to-crate mapping, chosen topology, rejected alternatives, and compatibility plan in the roadmap. See [Layout and governance](references/layout-and-governance.md).
### 2. Build inventories before implementation
Create separate machine-readable or tabular inventories for:
- Java Maven/Gradle modules and Rust crates.
- The source-module-to-crate mapping and why root-flat, hybrid, or contained/grouped paths fit this project.
- Java packages and Rust module directories.
- Classes, interfaces, enums, records, annotations, exceptions, and relevant inner types.
- For every object, the deterministic Rust path after removing the organization/module package root and retaining the final two remaining segments (or one/zero when fewer remain).
- Public/protected constructors and methods, including every overload.
- Parameter names, order, generic bounds, nullability, defaults, varargs, checked exceptions, and return contracts.
- Existing object, constructor, method, generic/value parameter, return, exception, metadata-tag, and semantic inline comments, with source anchors.
- Examples, tests, fixtures, scripts, configuration, resources, service descriptors, and docs.
- A source-test case manifest and asset manifest; record every concrete case and asset path/hash before copying resources byte-for-byte into Rust.
- A non-published `<project>-test` package owning project-level source replay, cross-component paths, and differential acceptance.
- Call paths and externally observable side effects.
Exclude `package-info`, generated sources, BOMs, aggregators, test support, facades, and Rust-only infrastructure only through explicit categories. Do not hide them by changing the denominator.
Freeze the inventory as the batch manifest before production edits. Cover the complete denominator, dependency order, shared mechanisms, component decisions, test disposition, and approved exceptions; do not discover scope object-by-object.
Resolve `SKILL_DIR` from this `SKILL.md`; never assume a fixed install path. Run commands from the Rust migration root and prefer repository-relative inputs/artifacts.
Run the static Rust layout audit as an early signal:
```bash
python3 "$SKILL_DIR/scripts/audit_migration_layout.py" \
--java-package-root ../java-project/source-module/src/main/java/org/example/module \
--rust-root . \
--retain-segments 2 \
--require-source-comments \
--fail-on-warning
```
The script calculates expected paths and distinguishes missing from misplaced
objects. It also detects non-snake-case paths, multi-object files, facade
definitions, wildcard imports, stub macros/panics, empty function bodies, and
missing Chinese source comments. Record an approved 501–800-line cohesion review
with repeated `--reviewed-large-file path/to/file.rs`; this never exempts a file
above 800 lines. Any strict blocker keeps migration completion blocked; a clean
scan still does not prove Java/Rust semantic parity.
### 3. Create four documents for every source module
When documentation writes are authorized, generate a documentation directory for each Java module before implementation:
```bash
python3 "$SKILL_DIR/scripts/scaffold_migration_docs.py" \
--module source-module \
--java-root ../java-project/source-module \
--java-package-root ../java-project/source-module/src/main/java/org/example/module \
--rust-root <selected-target-crate-or-workspace> \
--output-dir docs/source-module \
--java-baseline <sha-or-tag> \
--rust-baseline <sha> \
--retain-segments 2
```
The command creates:
1. `迁移路线图.md` — scope, baselines, phases, dependencies, risks, and evidence gates.
2. `对象级对照表.md` — every Java object and its Rust file/type/status.
3. `语义迁移对照表.md` — every behavior family and its Rust-native implementation.
4. `对象名称一致性检查.md` — counts, missing/extra/merged objects, names, methods, parameters, and logic gaps.
Populate every placeholder from source evidence. A generated template is
`DRAFT`, not evidence and never completion. Keep documents synchronized with
code in the same change. Templates:
- [Migration roadmap](assets/templates/迁移路线图.md)
- [Object mapping](assets/templates/对象级对照表.md)
- [Semantic mapping](assets/templates/语义迁移对照表.md)
- [Name consistency audit](assets/templates/对象名称一致性检查.md)
Every one of the four documents must be independently detailed and must contain
the module's current migration contract: source/Rust SHAs, exact object
denominator, target root, `retain_segments = 2`, status snapshot, strict
completion rules, and that document's responsibility. Reject a title-only,
count-only, or placeholder-only document. As an anti-summary floor, require at
least three substantive level-2 sections plus an evidence table or task matrix;
use a repository-configured size floor (45 nonblank lines by default) while
allowing a proportionally smaller generated object table for a genuinely tiny
module.
Every document must show separate Java and Rust baselines, its last-audited date,
and a document status. Every migrated/verified row needs an evidence anchor:
source file or symbol, target file or symbol, test/oracle, exact command, and
artifact where applicable. During implementation, do not upgrade rows one at a
time. After the batch freeze, cross-check all four documents against the current
Rust SHA and update statuses in one consolidated pass. A later count table must
not silently contradict a technical-requirements document or an earlier semantic
gap.
Keep exactly one current four-document set at the module root. Do not leave
`*-历史详细版.md` or a second `history/**/<current-name>.md` beside it. Merge
useful old package grouping, design context, and decision history into a clearly
delimited “历史设计附录” in the corresponding current document. The generated
current-fact region must remain first and regeneration must preserve the
appendix. Old counts, paths, statuses, tests, and completion marks never override
current facts.
Treat scaffold `--force` as destructive and use it only for a disposable,
untouched `DRAFT`; never use it to merge or refresh a populated current
document. Merge historical details into the current document in place and
preserve both its generated fact region and existing appendix.
### 4. Classify every object honestly
Use these object states consistently. Keep verification levels (`V0`–`V7`)
separate; do not invent a friendlier state or translate a test result directly
into an object state.
| State | Meaning |
|---|---|
| `MISSING` | Expected Rust object file does not exist |
| `MISPLACED` | Same-name file/type exists but not at the deterministic expected path |
| `STUB` | Shape or placeholder exists but real behavior is absent |
| `PARTIAL` | Real behavior exists but methods, callbacks, errors, ordering, lifecycle, or integration semantics are incomplete |
| `UNVERIFIED` | File/logic exists but source comments, object boundary, or semantic test evidence is insufficient |
| `IMPLEMENTED` | Expected path, one-object boundary, real complete logic, Chinese source semantics, and current semantic tests all exist |
| `DEPENDENCY_REUSED` | A pinned dependency provides the exact capability; crate/version or commit, upstream symbol, adapter, and local integration test are recorded |
| `PLATFORM_NA` | The capability is genuinely JVM/bytecode/class-loader/platform-only and explicit evidence records why no Rust object applies |
| `RUST_EXTENSION` | Intentional Rust-only capability, excluded from Java parity numerator |
Only `IMPLEMENTED`, `DEPENDENCY_REUSED`, and `PLATFORM_NA` count as handled
source objects. `MISSING`, `MISPLACED`, `STUB`, `PARTIAL`, and `UNVERIFIED` are
incomplete. `RUST_EXTENSION` never enters the Java denominator.
Allow a planned placeholder only when the user explicitly approves it. Record
the blocker in the roadmap, but keep each affected object in its factual
`MISSING` or `STUB` state. A blocker is metadata, not a completion-like object
state.
Never upgrade from `MISSING` merely because an object name appears in a manifest,
facade, `lib.rs`, `mod.rs`, re-export, generated registry, or compatibility
module. Never upgrade from `UNVERIFIED` merely because `cargo test` is green.
### 5. Decide component replacements from contracts
Do not map framework names directly. For every external Java component or framework subsystem:
The Java source module remains authoritative for object names, package
structure, and public contracts. A Rust dependency is only an implementation
reuse boundary. Do not restructure the migration around the dependency's file
tree and do not copy dependency-owned implementations into local files merely
to improve parity counts. For AOP-like work, for example, Spring defines the
Advice/Interceptor/Advisor object inventory while an aspect crate may satisfy
specific runtime symbols through `DEPENDENCY_REUSED`.
1. Extract the behavior contract: API shape, wire/storage format, ordering, failure taxonomy, lifecycle, transactions, concurrency, cancellation, backpressure, security, observability, and deployment assumptions.
2. Choose a replacement shape: standard library, direct crate, wrapped crate, trait plus adapters, explicit registry/SPI, compile-time macro/code generation, application-host responsibility, or proven `PLATFORM_NA`.
3. Check the common mapping table and candidate catalog as discovery starting points, never as automatic approval.
4. When no verified mapping fits, generate several English capability/protocol/constraint queries, search crates.io and companion primary sources, and shortlist five to ten candidates across std, direct crate, wrapper, trait/adapters, code generation, host responsibility, and exclusion shapes.
5. Route crates.io metadata collection and ecosystem-health comparison to `rust-crate-discovery` when available. Apply migration-specific contract and compatibility gates here; its numeric health score does not select the replacement.
6. Reject candidates that fail a required contract, license, MSRV/target, runtime/blocking, protocol, security, maintenance/ownership, or dependency-graph constraint before scoring.
7. Compare viable candidates using semantic fit, maintenance, adoption, docs/tests, project compatibility, security/supply chain, maturity, cost, and exit strategy. Interpret downloads, reverse dependencies, stars, release recency, and commits as contextual signals, not proof.
8. Spike the highest-risk semantic path for the top candidates before committing the architecture.
9. Record search queries and date, ownership, version/features, per-dimension evidence/confidence, rejected alternatives, escape hatch, and rollback plan.
10. Promote an object to `DEPENDENCY_REUSED` only when the exact upstream symbol,
pinned dependency evidence, adapter boundary, and local integration test are
all recorded. “The ecosystem has it” or “semantically similar” remains
`UNVERIFIED`.
For multiple target frameworks, define a framework-neutral contract and thin adapters, then run one shared conformance suite against every adapter. Keep runtime traits/types separate from thin procedural macros and generated code.
Read [Component replacement decision SOP](references/component-replacement-sop.md), [crate replacement discovery and evaluation](references/crate-replacement-discovery.md), and [Component candidate catalog](references/component-candidate-catalog.md) before choosing or approving a third-party replacement. The catalog is discovery input, never an approval list; re-verify release, maintenance, license, MSRV, targets, advisories, unsafe/build-script surface, and required contracts at decision time.
### 6. Complete the declared batch in one semantic implementation pass
Read [Layout and migration rules](references/layout-and-governance.md),
[Directory path alignment patterns and error catalog](references/directory-path-alignment.md),
and [Semantic mappings](references/semantic-mappings.md) before changing code. Then
execute the entire frozen batch without object-level acceptance pauses:
1. Establish the target module tree, shared errors, traits, registries, adapters,
serialization rules, concurrency model, and dependency boundaries once.
2. Implement every mapped Java object and operation in dependency order. Keep
exactly one primary `.rs` file per Java object and real logic in the
corresponding object or explicit collaborator files.
3. Copy and translate JavaDoc semantics into Chinese Rust doc comments across
the batch. Migrate every existing object comment, constructor/method comment,
generic and value `@param`, `@return`, `@throws`, `@since`, `@deprecated`,
relevant `@see`, and semantic inline comment without omission. Preserve the
parameter-specific contracts. Keep Java-to-Rust name, signature, and exception
mappings in the four migration documents; keep generated Rust documentation
Rust-native.
4. Implement all mapped overload variants, examples, fixtures, source-test
counterparts, Rust-specific obligations, and risk-driven tests as batch
artifacts, but do not execute validation yet. Preserve 100% of source test
inputs, assertions, error expectations, state, side effects, and cleanup.
Copy source test files/data/resources without editing them; put any
Rust-specific derivatives in separate files.
5. Maintain one deferred-issues ledger. Continue through local uncertainties;
pause only for a blocker that changes the frozen public contract,
architecture, dependency policy, or authorized scope.
6. When every non-exempt manifest row has real implementation, freeze the Rust
batch. Only then update the four documents in bulk and enter audit.
During this pass, do **not** run `cargo check`, tests, Clippy, coverage,
differential comparison, per-object CodeGraph re-queries, or per-object
completion reviews. Do not report an object as accepted merely because its file
was edited. Recovery commits are allowed, but they are not verification gates.
Read [Comment migration contract and example](references/comment-migration.md)
before migrating documentation. Treat missing source comments as migration gaps,
not optional cleanup.
### 7. Preserve naming and overload intent
- Use `snake_case` for Rust directories, files, methods, and parameters.
- Use `PascalCase` for Rust types.
- Map `loadOrCreateAgentState(slotKey)` to `load_or_create_agent_state(slot_key)`.
- Remove the organization and declared source-module package root, then retain
exactly the final two remaining package segments. Retain one when only one
remains and place root-package objects at the crate root. Example:
`factory/xml/support/Foo.java` → `xml/support/foo.rs`;
`propertyeditors/PatternEditor.java` → `propertyeditors/pattern_editor.rs`.
- Keep `lib.rs` and `mod.rs` as declarations and re-exports only.
- Keep one Java class/interface/enum/record per Rust file; an inner builder tightly owned by the primary type may remain with it.
- Treat 500 physical lines as a cohesion-review threshold and 800 as the authored-file blocker. Split by responsibility rather than line ranges; no subordinate file may introduce another migrated object.
- Enable `clippy::too_many_lines` as a function/method review signal at its 100-line default; refactor long routines when they combine distinct responsibilities.
- Use Rust's idiomatic test organization: colocate focused unit tests in `#[cfg(test)]` modules, and place public-boundary, cross-module, differential, host, and whole-project tests in `tests/` or a test/testkit package.
- Record every intentional rename in both object and name-consistency documents.
Rust has no method overloading. Keep one canonical snake_case name only when the signatures have one semantic operation. Give additional variants stable semantic suffixes such as `_with_charset`, `_into`, or `_from_reader`; record the exact Java signature mapped to each Rust function. Never collapse overloads that differ in defaults, validation, side effects, or error behavior.
Apply the local `rust-api-design` conventions to every migrated public Rust
surface. Do not mechanically translate JavaBean accessors: prefer `name()` over
`get_name()` (except genuine lookup operations), `set_name(value)` for controlled
mutation, `name_mut()` only when it cannot bypass invariants,
`into_name()`/`into_inner()` for ownership transfer, semantic boolean predicates,
and chainable builders. Use `as_`/`to_`/`into_`, `From`/`TryFrom`/`AsRef` and
`IntoIterator` by their Rust meanings; do not use `Deref` to emulate Java
inheritance. Expose fields directly only when invariants and API evolution permit
it. Preserve validation, visibility, side effects, exceptions, synchronization,
and lazy-computation behavior.
When a script, expression engine, serializer, reflection facade, or other
compatibility surface exposes Java property semantics, keep the Rust API
idiomatic and implement the old field/getter/setter behavior in an explicit
registry or member resolver. Record this relationship as mapping form
`ADAPTED`; this is orthogonal to completion status and still requires
`IMPLEMENTED`, `UNVERIFIED`, or another factual state.
Read [Rust API and JavaBean property adaptation](references/rust-api-adaptation.md)
before migrating getters, setters, builders, or script-visible properties.
### 8. Translate mechanisms, not frameworks literally
Use this table as a common starting point, then verify the exact contract and
current crate evidence:
| Java responsibility | Rust starting point |
|---|---|
| Jackson JSON annotations/modules | `serde`, `serde_json`, project-owned custom serializers |
| Jackson XML / JAXB-style XML | `quick-xml` plus explicit namespace, attribute, mixed-content, and ordering logic |
| `null`, checked exceptions | `Option<T>`; typed `thiserror` enums and `Result` |
GitHub에서 보기