organize-modules
Applies clean API patterns to Rust module structures.
Install
mkdir -p .claude/skills/organize-modules && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/2483" && unzip -o skill.zip -d .claude/skills/organize-modules && rm skill.zipInstalls to .claude/skills/organize-modules
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Apply private modules with public re-exports (barrel export) pattern for clean API design. Includes conditional visibility for docs and tests. Use when creating modules, organizing mod.rs files, or before creating commits.Key capabilities
- →Apply barrel export pattern for public APIs
- →Implement conditional visibility for docs and tests
- →Organize mod.rs files for clean module structure
- →Control rustfmt behavior for manual alignment
How it works
The skill refactors module visibility by using private modules for implementation details and public re-exports for the API, while applying conditional compilation flags for testing and documentation.
Inputs & outputs
When to use organize-modules
- →Clean up mod.rs files in a Rust project
- →Expose public APIs while keeping internals private
- →Organize module visibility for unit testing
About this skill
Module Organization Best Practices
When to Use
- Creating new Rust modules
- Refactoring module structure
- Organizing mod.rs files
- Reviewing code that exposes internal structure
- Making private types visible to documentation
- Before creating commits with module changes
- When user says "organize modules", "refactor modules", "fix module structure", etc.
Instructions
Follow these patterns for clean, maintainable module organization:
Step 1: Apply the Recommended Pattern
Prefer private modules with public re-exports (also known as the barrel export pattern) as the default pattern.
This provides a clean API while maintaining flexibility to refactor internal structure.
// mod.rs - Module coordinator
// Private modules (hide internal structure)
mod constants;
mod types;
mod helpers;
// Public re-exports (expose stable API)
pub use constants::*;
pub use types::*;
pub use helpers::*;
What this achieves:
- Clean, flat API for users
- Internal structure is hidden and can be refactored freely
- No namespace pollution from module names
Step 2: Control Rustfmt Behavior (When Needed)
For mod.rs files with deliberate manual alignment, prevent rustfmt from reformatting:
// mod.rs
#![rustfmt::skip]
// Private modules
mod constants;
mod types;
mod helpers;
// Public re-exports
pub use constants::*;
pub use types::*;
pub use helpers::*;
When to use rustfmt skip:
- Large
mod.rsfiles with many exports - Deliberately structured code alignment for clarity
- Manual grouping of related items (e.g., test fixtures)
- Files where organization conveys semantic meaning
When NOT to use:
- Small, simple mod.rs files
- When automatic formatting is preferred
Step 3: Apply Conditional Visibility for Docs and Tests
When you need a module to be:
- Private in production builds (encapsulation)
- Public for documentation (rustdoc links work)
- Public for tests (test code can access internals)
Use conditional compilation:
// mod.rs - Conditional visibility
#[cfg(any(test, doc))]
pub mod internal_parser;
#[cfg(not(any(test, doc)))]
mod internal_parser;
// Re-export items for the flat public API
pub use internal_parser::*;
How this works:
- In doc builds: Module is public → rustdoc can see and link to it
- In test builds: Module is public → tests can access internals
- In production builds: Module is private → internal implementation detail
This pattern is frequently used with the write-documentation skill when fixing documentation
links to private types.
When to Omit the Fallback Branch
You can skip the #[cfg(not(any(test, doc)))] fallback when the module has no code to compile
in production:
// ✅ OK to skip fallback - documentation-only module (no actual code)
#[cfg(any(test, doc))]
pub mod integration_tests_docs;
// ✅ OK to skip fallback - all submodules are #[cfg(test)] anyway
#[cfg(any(test, doc))]
pub mod integration_tests;
Keep the fallback when the module contains code that must compile in production (even if private):
// ✅ Need fallback - module has actual code used in production
#[cfg(any(test, doc))]
pub mod internal_parser;
#[cfg(not(any(test, doc)))]
mod internal_parser;
pub use internal_parser::*; // Re-exports need the module to exist!
Integration Tests Module
For modules that contain PTY integration tests (typically under integration_tests/),
follow these rules:
- Dedicated Files: Each complex test gets its own file.
- Module Documentation: Use
//!at the top of the file to document the test's intent. - Run Instructions: Always include a "Run with:" section at the top of the file
(see
write-documentationskill for details). - Conditional Visibility: Parent modules must make these test modules public for doc and test builds.
// integration_tests/mod.rs
#[cfg(any(test, doc))]
pub mod pty_feature_test;
Platform-Specific Modules with Cross-Platform Docs
For modules that are platform-specific but should have docs generated on all platforms, use
any(doc, ...) to separate documentation from runtime requirements:
// ✅ Linux-only runtime, but docs build on all platforms
#[cfg(any(doc, all(target_os = "linux", test)))]
pub mod input;
#[cfg(all(target_os = "linux", not(any(test, doc))))]
mod input;
// Re-export also needs the doc condition
#[cfg(any(target_os = "linux", doc))]
pub use input::*;
Key insight: rustdoc runs the Rust compiler internally. When you write
#[cfg(all(target_os = "linux", any(test, doc)))], the target_os = "linux" check still excludes
macOS/Windows even during doc builds. The doc cfg flag doesn't override other conditions—it's
just another flag you can check.
The fix: Use any(doc, ...) to make doc an alternative path:
any(doc, all(target_os = "linux", test))means: "docs on any platform OR tests on Linux"all(target_os = "linux", any(test, doc))means: "Linux AND (tests OR docs)" — still requires Linux!
When you see broken doc links for platform-specific modules:
// ❌ Broken: Docs won't generate on macOS
#[cfg(all(target_os = "linux", any(test, doc)))]
pub mod linux_only_module;
// ✅ Fixed: Docs generate on all platforms (if module code is platform-agnostic)
#[cfg(any(doc, all(target_os = "linux", test)))]
pub mod linux_only_module;
#[cfg(all(target_os = "linux", not(any(test, doc))))]
mod linux_only_module;
⚠️ Unix Dependency Caveat
The cfg(any(doc, ...)) pattern above assumes the module's code compiles on all platforms.
When the module uses Unix-only APIs (e.g., mio::unix::SourceFd, signal_hook,
std::os::fd::AsRawFd), restrict doc builds to Unix:
// Module uses Unix-only APIs — restrict doc builds to Unix platforms
#[cfg(any(all(unix, doc), all(target_os = "linux", test)))]
pub mod input;
#[cfg(all(target_os = "linux", not(any(test, doc))))]
mod input;
#[cfg(any(target_os = "linux", all(unix, doc)))]
pub use input::*;
Three-tier hierarchy:
| Module dependencies | Pattern | Docs: Linux | Docs: macOS | Docs: Windows |
|---|---|---|---|---|
| Platform-agnostic | cfg(any(doc, ...)) | ✅ | ✅ | ✅ |
| Unix APIs | cfg(any(all(unix, doc), ...)) | ✅ | ✅ | excluded |
| Linux-only APIs | cfg(any(all(target_os = "linux", doc), ...)) | ✅ | excluded | excluded |
Rule of thumb: Match your doc cfg guard to your dependency's cfg guard in Cargo.toml.
Apply at all levels — If the module is nested, both parent and child need the visibility change. Also update any re-exports:
// Parent module
#[cfg(any(doc, all(target_os = "linux", test)))]
pub mod integration_tests;
// Child modules inside integration_tests/mod.rs
#[cfg(any(doc, all(target_os = "linux", test)))]
pub mod pty_input_test;
// Re-exports
#[cfg(any(target_os = "linux", doc))]
pub use integration_tests::*;
Step 4: Handle Transitive Visibility
Important: If a conditionally public module links to another module in its documentation, that target module must also be conditionally public.
// mod.rs
#[cfg(any(test, doc))]
pub mod paint_impl; // Contains docs that link to diff_chunks
#[cfg(not(any(test, doc)))]
mod paint_impl;
#[cfg(any(test, doc))]
pub mod diff_chunks; // Must also be conditionally public!
#[cfg(not(any(test, doc)))]
mod diff_chunks;
// Re-export for public API
pub use paint_impl::*;
pub use diff_chunks::*;
Why: Rustdoc needs to resolve all links in documentation. If paint_impl docs link to
diff_chunks, rustdoc must be able to see diff_chunks.
Step 5: Reference in Rustdoc
When linking to conditionally public modules in documentation, use the mod@ prefix:
/// See [`internal_parser`] for implementation details.
///
/// [`internal_parser`]: mod@crate::internal_parser
See the write-documentation skill for complete details on rustdoc links.
Step 6: Multi-Level Barrel Exports and Rustdoc Search
When rustdoc generates documentation, the search index includes all public items and
modules. For multi-level barrel exports (pub mod intermediate; pub use intermediate::*;),
the search index resolves the "shortest public path" for items. But rustdoc only generates
HTML pages at the canonical definition path, not at the flattened re-export path.
This means searching for an item re-exported via a barrel might produce a link to a page
that doesn't exist (e.g., core/ansi/csi/index.html instead of
core/ansi/constants/csi/index.html).
The fix: Use #[doc(inline)] to re-export submodules at the parent level, but only when
the intermediate module is a well-documented organizational hub (has module-level //! docs,
organization tables, etc.) AND its submodules are pub mod.
| Intermediate module characteristics | Action | Why |
|---|---|---|
| Public, has module docs, organization tables | Add #[doc(inline)] | Submodules are discoverable via search; pages must exist |
Private with pub use *; only (pure barrel) | No action needed | Submodules aren't in the search index at all |
| Public but no module docs (structural only) | No action needed | Not worth the doc noise; users won't search for these |
In the parent module's mod.rs, add explicit #[doc(inline)] re-exports alongside the
existing glob re-export:
// Existing: keeps flat item access working
pub use constants::*;
// New: creates rustdoc pages at th
---
*Content truncated.*
When not to use it
- →When the module structure is simple and does not require encapsulation
- →When automatic rustfmt formatting is preferred for all files
Limitations
- →Requires manual updates when adding new modules to the barrel export
- →Conditional visibility patterns can increase complexity in mod.rs files
How it compares
It provides a standardized pattern for managing Rust module visibility, preventing internal implementation details from leaking into the public API.
Compared to similar skills
organize-modules side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| organize-modules (this skill) | 1 | 2mo | Review | Intermediate |
| m06-error-handling | 1 | 6mo | Review | Intermediate |
| m04-zero-cost | 0 | 6mo | No flags | Intermediate |
| m05-type-driven | 0 | 6mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by r3bl-org
View all by r3bl-org →You might also like
m06-error-handling
actionbook
CRITICAL: Use for error handling. Triggers: Result, Option, Error, ?, unwrap, expect, panic, anyhow, thiserror, when to panic vs return Result, custom error, error propagation, 错误处理, Result 用法, 什么时候用 panic
m04-zero-cost
actionbook
CRITICAL: Use for generics, traits, zero-cost abstraction. Triggers: E0277, E0308, E0599, generic, trait, impl, dyn, where, monomorphization, static dispatch, dynamic dispatch, impl Trait, trait bound not satisfied, 泛型, 特征, 零成本抽象, 单态化
m05-type-driven
actionbook
CRITICAL: Use for type-driven design. Triggers: type state, PhantomData, newtype, marker trait, builder pattern, make invalid states unrepresentable, compile-time validation, sealed trait, ZST, 类型状态, 新类型模式, 类型驱动设计
minimize-rust-ffi-crate-surface
RediSearch
Remove Rust-defined C symbols that are either unused or only used in C/C++ unit tests.
upgrade-oxc
rolldown
Upgrade oxc, run codegen, and fix any breaking changes.
clean-code
gregoire78
Apply clean code practices in this repository with small, safe refactors, explicit naming, reduced complexity, and behavior-preserving changes. Use for readability improvements, technical debt cleanup, and maintainability reviews in Rust code.