Skip to main content

split_modules/
model.rs

1//! Core data types shared across the split pipeline.
2
3use std::path::PathBuf;
4
5/// How the parent module owns its submodule files on disk.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Layout {
8    /// `foo.rs` → submodule files live in a sibling `foo/` directory.
9    Adjacent,
10    /// `lib.rs` / `main.rs` / `mod.rs` → submodule files live in the same directory.
11    DirOwner,
12}
13
14/// User-facing configuration for a split run.
15#[derive(Debug, Clone)]
16pub struct Config {
17    /// Run `cargo check` after splitting and roll back if it fails.
18    pub verify: bool,
19    /// Run `rustfmt` on generated/changed files when available.
20    pub rustfmt: bool,
21    /// Print the plan but do not touch the filesystem.
22    pub dry_run: bool,
23    /// In recursive mode, only split files that would yield at least this many module files.
24    pub min_groups: usize,
25}
26
27impl Default for Config {
28    fn default() -> Self {
29        Config {
30            verify: true,
31            rustfmt: true,
32            dry_run: false,
33            min_groups: 2,
34        }
35    }
36}
37
38/// How an item's visibility must be rewritten when it moves into a child module,
39/// so the parent can still re-export it at its original visibility.
40#[derive(Debug, Clone)]
41pub struct VisEdit {
42    /// Byte range *within the item's own source slice* to replace.
43    /// A zero-width range means "insert at this offset".
44    pub rel_start: usize,
45    pub rel_end: usize,
46    /// Replacement / inserted text (e.g. `pub(super) ` or `pub(crate)`).
47    pub text: String,
48}
49
50/// A re-export emitted in the parent module to preserve the original namespace.
51#[derive(Debug, Clone)]
52pub struct ReExport {
53    /// Visibility rendered exactly as the item originally declared it.
54    pub vis: String,
55    /// The item's identifier.
56    pub name: String,
57    /// `#[cfg(...)]` attributes to replicate onto the re-export, if any.
58    pub cfg_attrs: Vec<String>,
59}
60
61/// A single top-level item that will move into a child module file.
62#[derive(Debug, Clone)]
63pub struct MovedItem {
64    /// Target file stem (snake_case); items sharing a stem share a file.
65    pub group: String,
66    /// Plain `//` comment block immediately preceding the item (already-doc comments
67    /// are part of `text`). May be empty.
68    pub leading_comment: String,
69    /// Raw source slice of the item, including doc-comments and attributes.
70    pub text: String,
71    /// Visibility rewrites to apply to `text` for the child file (item + members).
72    pub vis_edits: Vec<VisEdit>,
73    /// Re-export to emit in the parent (None for `impl` blocks).
74    pub reexport: Option<ReExport>,
75    /// Original order index in the source file.
76    pub order: usize,
77}
78
79/// One generated child module file.
80#[derive(Debug, Clone)]
81pub struct GroupFile {
82    pub stem: String,
83    /// Indices into [`SplitPlan::moved`], in original source order.
84    pub item_indices: Vec<usize>,
85}
86
87/// The full plan for splitting one source file.
88#[derive(Debug, Clone)]
89pub struct SplitPlan {
90    pub source_path: PathBuf,
91    pub layout: Layout,
92    /// Directory where child files are written.
93    pub out_dir: PathBuf,
94    /// New parent file contents (the original file rewritten as a module index).
95    pub parent_contents: String,
96    /// Items being moved.
97    pub moved: Vec<MovedItem>,
98    /// Generated files (stem + which items).
99    pub files: Vec<GroupFile>,
100}
101
102impl SplitPlan {
103    /// Whether this plan actually changes anything.
104    pub fn is_noop(&self) -> bool {
105        self.files.len() < 2
106    }
107}
108
109/// Outcome of attempting to split one file.
110#[derive(Debug)]
111pub enum FileOutcome {
112    Split { files: Vec<PathBuf> },
113    Skipped(String),
114    RolledBack(String),
115}