Reorder top-level items in Rust source files from the command line.
Parses .rs files with syn, assigns each top-level item a numeric ordinal, and lets you rearrange them via list, move, and order commands. Comments travel with their associated items. A safety check guarantees no non-empty lines are lost or duplicated.
brew install umwelt-ai/tap/rust-reorder
cargo install rust-reorder
git clone https://github.com/umwelt-ai/rust-reorder.git
cd rust-reorder
cargo build --release
The binary is at target/release/rust-reorder.
rust-reorder <COMMAND>
Commands:
list List top-level items with their ordinals
move Move one item to a new position
order Fully reorder items by specifying all ordinals in the desired order
rust-reorder list <FILE>
Prints each top-level item with its ordinal, kind, and name. Output is tab-separated (ORDINAL\tKIND\tNAME), designed for scripting.
$ rust-reorder list src/lib.rs
0 use std::fmt
1 struct Config
2 impl Config
3 impl Display for Config
4 fn main
rust-reorder move <FILE> <ORDINAL> --after <ORDINAL>
rust-reorder move <FILE> <ORDINAL> --before <ORDINAL>
Moves the item at the given ordinal to a new position relative to another item. Exactly one of --after or --before must be specified. The file is modified in place (atomically).
# Move item 4 to after item 0
rust-reorder move src/lib.rs 4 --after 0
# Move item 4 to before item 1
rust-reorder move src/lib.rs 4 --before 1
rust-reorder order <FILE> <ORDINALS>
Specifies the complete output order as a comma-separated list of ordinals. Every ordinal must appear exactly once.
rust-reorder order src/lib.rs 4,0,1,2,3
Every top-level syn::Item variant is recognized:
| Syntax | Kind | Name |
|---|---|---|
fn main() |
fn |
main |
pub async fn serve() |
fn |
serve |
struct Config |
struct |
Config |
enum ErrorKind |
enum |
ErrorKind |
trait Parser |
trait |
Parser |
impl Config |
impl |
Config |
impl Display for Config |
impl |
Display for Config |
impl<T> From<T> for Config |
impl |
From for Config |
const MAX: usize = 10 |
const |
MAX |
static GLOBAL: Mutex<...> |
static |
GLOBAL |
type Result<T> = ... |
type |
Result |
mod tests |
mod |
tests |
use std::io |
use |
std::io |
extern crate alloc |
extern crate |
alloc |
extern "C" { ... } |
extern |
"C" |
union MyUnion |
union |
MyUnion |
macro_rules! my_macro |
macro |
my_macro |
Visibility (pub, pub(crate)), async, and generics are stripped from names. impl names show the trait and self type without generic parameters.
-
Parse.
syn::parse_fileproduces an AST. Each item's byte range is extracted fromproc_macro2span locations. -
Pin comments. The gap between consecutive items (blank lines,
//comments) is attached to the item below it. Doc comments (///) and attributes (#[...]) are part of the syn span and don't need separate handling. -
Identify regions. Inner attributes (
#![...]) and module doc comments (//!) at the top of the file form the preamble. Content after the last item forms the trailer. Both are preserved in place and never reordered. -
Compute permutation.
movebuilds a permutation by removing the source item and reinserting it at the target position.ordertakes the permutation directly from the user. -
Emit. The output is assembled by slicing the original source string according to the permutation. No content is generated — only sliced and rearranged.
-
Safety check. Before writing, the multiset of non-empty lines in the output is compared against the original. If they differ, the operation is aborted.
-
Atomic write. A tempfile is created in the same directory as the target, written, flushed, then atomically renamed over the original.
The tool fails immediately on:
- Syn parse failure (invalid Rust syntax)
- Non-UTF-8 input
- Out-of-range ordinals
- Same source and anchor in
move - Missing, duplicate, or out-of-range ordinals in
order - Safety invariant violation
- File not found
Error messages include the specific values involved and what was expected:
Error: ordinal 99 is out of range: file has 6 items (valid: 0..5)
Error: duplicate ordinal 1: each item must appear exactly once
Error: expected 6 ordinals, got 3: all items must be listed exactly once
- Top-level items only. Nested items (e.g. functions inside
implblocks) are not individually addressable. - No
\r\nsupport. Files with Windows line endings may not round-trip correctly. - Grouped use imports (
use std::{io, fmt}) show{...}as the name portion. - Non-path self types in
implblocks (e.g.impl (i32, i32)) showunknown. - Files without a trailing newline may gain one after reordering.
- No dry-run mode. The
moveandordercommands modify the file in place.
cargo test
Unit tests cover parsing, permutation logic, emission, and safety checks. Integration tests exercise the CLI binary via std::process::Command.