Skip to content

refactor: split src/core/mv.rs into responsibility-oriented submodules #10

@StudentWeis

Description

@StudentWeis

Current State

src/core/mv.rs is ~1750 lines and is the heaviest core file in the repo. It mixes at least six concerns in one module:

  • path validation (validate_move_paths, resolve_destination, canonicalize_destination)
  • case-only rename handling (resolve_case_only_destination, relative_path_preserving_filename_case, plan_case_only_external_replacements)
  • replacement planning for external refs, internal links, and directory moves (plan_external_replacements, plan_internal_replacements, plan_directory_replacements, plus all the build_*_replacement helpers and line-cache bookkeeping)
  • file rewriting and line-ending preservation (apply_replacements, split_lines_preserving_endings, LineEnding)
  • transactional execution with rollback (execute_with_rollback, try_rename_regular_file{,_with}, RegularFileMoveMethod)
  • preview / dry-run reporting (build_move_preview, print_dry_run_report)

Even when the logic is correct, this layout makes every future bug fix or review noticeably more expensive.

Target State

Split src/core/mv.rs into a module directory organized by responsibility, keeping the public API (pub fn mv, pub fn preview_move) and observable behavior completely unchanged:

src/core/mv/
├── mod.rs        # public entry points (mv / preview_move) + regular/case_only/directory orchestration
├── validate.rs   # path resolution and validation
├── case_only.rs  # case-only rename detection + planning
├── plan.rs       # ReplacementPlan, all plan_*/build_*_replacement helpers, line cache
├── apply.rs      # apply_replacements, line-ending preservation, rename fallback, execute_with_rollback
└── preview.rs    # build_move_preview, print_dry_run_report

Transaction data model (MoveTransaction) already lives in src/core/model/move_transaction.rs and is reused as-is — no new transaction module is introduced.

Rationale

Concrete pain points today:

  • Reading the file end-to-end is required to understand any single flow (regular move vs case-only rename vs directory move), because the three orchestrators and all their helpers are interleaved.
  • The mod tests block at the end of the file is already internally sectioned by comments (apply_replacements tests, build_link_replacement tests, etc.), which is a direct signal that the production code wants the same seams.
  • Any future change to, for example, line-ending handling, has to be reviewed against unrelated planning code in the same diff.

Splitting by responsibility — not by "file size" — keeps each concern reviewable on its own and lets later stages layer small local abstractions safely.

Scope

mv

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorInternal cleanup / technical-debt repayment with no user-visible change

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions