Skip to content

Commit e1693df

Browse files
committed
first commit
0 parents  commit e1693df

File tree

5 files changed

+376
-0
lines changed

5 files changed

+376
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target

Cargo.lock

Lines changed: 193 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "sortn"
3+
version = "1.0.0"
4+
authors = ["Dorian Meric"]
5+
repository = "https://github.com/dorianmeric/sortn"
6+
license = "MIT"
7+
description = "natural sort CLI"
8+
readme = "README.md"
9+
edition = "2024"
10+
11+
[dependencies]
12+
clap = { version = "4.5.54", features = ["derive"] }
13+
natord = "1.0.9"
14+
15+
[profile.release]
16+
lto = true
17+
codegen-units = 1
18+
panic = "abort"
19+
20+
[[bin]]
21+
name = "sortn"
22+
path = "src/main.rs"

README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# sortn
2+
3+
`sortn` is a fast, command-line utility written in Rust that performs **natural sorting** on input lines. Unlike standard lexicographical sorting—which would sort "file10.txt" before "file2.txt"—`sortn` treats multi-digit numbers as single numeric values, resulting in a more human-friendly order (1, 2, 10).
4+
5+
## Features
6+
7+
* **Natural Ordering**: Correctly handles numeric sequences within strings.
8+
* **Case Insensitivity**: Optional flag (`-i`) to ignore character casing during comparison.
9+
* **Reverse Sort**: Quickly invert the sorting order with the `-r` flag.
10+
* **Modern CLI**: Built with **Clap v4**, providing a clean interface and automatic help generation.
11+
12+
## Installation
13+
14+
Ensure you have the Rust toolchain installed.
15+
16+
1. Clone this repository.
17+
2. Build the release binary:
18+
```bash
19+
cargo build --release
20+
21+
```
22+
23+
24+
3. Move the binary to your path:
25+
```bash
26+
cp target/release/sortn /usr/local/bin/
27+
28+
```
29+
30+
31+
32+
## Usage
33+
34+
`sortn` reads from standard input (`stdin`) and writes the sorted result to standard output (`stdout`).
35+
36+
### Basic Sorting
37+
38+
```bash
39+
$ cat files.txt
40+
file10.txt
41+
file2.txt
42+
file1.txt
43+
44+
$ cat files.txt | sortn
45+
file1.txt
46+
file2.txt
47+
file10.txt
48+
49+
```
50+
51+
### Reverse Order (`-r`)
52+
53+
```bash
54+
$ cat files.txt | sortn -r
55+
file10.txt
56+
file2.txt
57+
file1.txt
58+
59+
```
60+
61+
### Case-Insensitive (`-i`)
62+
63+
```bash
64+
$ echo -e "B\na\nC" | sortn -i
65+
a
66+
B
67+
C
68+
69+
```
70+
71+
## Options
72+
73+
| Flag | Long Flag | Description |
74+
| --- | --- | --- |
75+
| `-r` | `--reverse` | Sort in reverse order. |
76+
| `-i` | `--ignore-case` | Perform case-insensitive natural sorting. |
77+
| `-h` | `--help` | Print help information. |
78+
| `-V` | `--version` | Print version information. |
79+
80+
## Comparison with GNU `sort`
81+
82+
While the standard GNU `sort` utility includes a `-V` (`--version-sort`) flag that achieves similar results, `sortn` offers several distinctions:
83+
84+
* **Simplicity**: `sortn` is a focused, lightweight tool specifically designed for natural sorting without the overhead of the dozens of flags found in GNU `sort`.
85+
* **Performance**: Written in Rust and utilizing the `natord` crate, `sortn` provides highly competitive performance for in-memory sorting tasks.
86+
* **Memory Usage**: Currently, `sortn` loads all input lines into memory to perform a global sort. For extremely large datasets that exceed available RAM, GNU `sort` (which uses disk-based merging) may be more suitable.
87+
88+
## Performance Benchmarks
89+
90+
`sortn` is optimized for speed by:
91+
92+
1. **Locking I/O**: Accessing `stdin` and `stdout` through locked buffers to minimize system call overhead.
93+
2. **Efficient Comparison**: Using the `natord` crate which is optimized for natural string comparison without unnecessary allocations.
94+
3. **Zero-Cost Abstractions**: Leveraging Rust's ownership model to manage string data efficiently during the sort process.
95+

src/main.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use clap::Parser;
2+
use std::io::{self, BufRead, Write};
3+
4+
/// A utility to perform natural sorting on input lines.
5+
#[derive(Parser, Debug)]
6+
#[command(
7+
name = "sortn",
8+
version,
9+
about = "Sorts lines naturally (e.g., '2' before '10')",
10+
long_about = None
11+
)]
12+
struct Args {
13+
/// Sort in reverse order
14+
#[arg(short, long)]
15+
reverse: bool,
16+
17+
/// Case-insensitive sorting
18+
#[arg(short, long)]
19+
ignore_case: bool,
20+
}
21+
22+
fn main() {
23+
let args = Args::parse();
24+
25+
let stdin = io::stdin();
26+
let stdout = io::stdout();
27+
28+
// 1. Collect all lines into memory as Strings
29+
// Natural sorting requires string comparison. We collect them into a Vec
30+
// so we can perform the sort operation on the whole collection.
31+
let mut lines: Vec<String> = stdin
32+
.lock()
33+
.lines()
34+
.map(|l| l.expect("Failed to read line from stdin"))
35+
.collect();
36+
37+
// 2. Perform natural sort
38+
// We use the natord crate for "natural" alphanumeric ordering.
39+
lines.sort_by(|a, b| {
40+
let cmp = if args.ignore_case {
41+
// Compare lowercase versions for case-insensitivity
42+
natord::compare(&a.to_lowercase(), &b.to_lowercase())
43+
} else {
44+
natord::compare(a, b)
45+
};
46+
47+
if args.reverse {
48+
cmp.reverse()
49+
} else {
50+
cmp
51+
}
52+
});
53+
54+
// 3. Output the sorted lines
55+
let mut out = stdout.lock();
56+
for line in lines {
57+
if let Err(e) = writeln!(out, "{}", line) {
58+
// Handle broken pipes (e.g., when piping to `head`) gracefully
59+
if e.kind() == io::ErrorKind::BrokenPipe {
60+
break;
61+
}
62+
panic!("Failed to write output: {}", e);
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)