@@ -33,15 +33,81 @@ struct Options {
33
33
) ]
34
34
dry_run : bool ,
35
35
36
- #[ arg( help = "Destination file" ) ]
37
- filepath : String ,
36
+ #[ arg(
37
+ short = 'u' ,
38
+ long = "unique" ,
39
+ help = "Merge all input files, sort and remove duplicates (like 'sort -u')"
40
+ ) ]
41
+ unique_mode : bool ,
42
+
43
+ #[ arg( help = "Destination file" , required_unless_present = "unique_mode" ) ]
44
+ filepath : Option < String > ,
45
+
46
+ #[ arg( help = "Additional input files for unique mode" , num_args = 0 ..) ]
47
+ additional_files : Vec < String > ,
38
48
}
39
49
40
50
fn main ( ) -> io:: Result < ( ) > {
41
51
let args = Options :: parse ( ) ;
42
52
53
+ if args. unique_mode {
54
+ // Handle unique mode (sort -u equivalent)
55
+ let mut all_lines = IndexSet :: new ( ) ;
56
+
57
+ // Process files if provided
58
+ if let Some ( filepath) = & args. filepath {
59
+ if let Ok ( lines) = load_file_content ( filepath) {
60
+ for line in lines {
61
+ if should_add_line ( & args, & all_lines, & line) {
62
+ all_lines. insert ( line) ;
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ // Process additional files
69
+ for file in & args. additional_files {
70
+ if let Ok ( lines) = load_file_content ( file) {
71
+ for line in lines {
72
+ if should_add_line ( & args, & all_lines, & line) {
73
+ all_lines. insert ( line) ;
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ // Process stdin
80
+ let stdin = io:: stdin ( ) ;
81
+ let mut handle = stdin. lock ( ) ;
82
+ let mut buffer = String :: new ( ) ;
83
+
84
+ while handle. read_line ( & mut buffer) ? > 0 {
85
+ let line = buffer. trim_end ( ) . to_string ( ) ;
86
+ if should_add_line ( & args, & all_lines, & line) {
87
+ all_lines. insert ( line) ;
88
+ }
89
+ buffer. clear ( ) ;
90
+ }
91
+
92
+ // Sort if requested
93
+ let mut final_lines: Vec < _ > = all_lines. into_iter ( ) . collect ( ) ;
94
+ if args. sort {
95
+ final_lines. sort_by ( |a, b| natsort:: compare ( a, b, false ) ) ;
96
+ }
97
+
98
+ // Output results
99
+ for line in final_lines {
100
+ println ! ( "{}" , line) ;
101
+ }
102
+
103
+ return Ok ( ( ) ) ;
104
+ }
105
+
106
+ // Original functionality for non-unique mode
107
+ let filepath = args. filepath . as_ref ( ) . expect ( "Destination file is required in non-unique mode" ) ;
108
+
43
109
// Ensure the directories in the filepath exist before attempting to open the file
44
- if let Some ( parent) = Path :: new ( & args . filepath ) . parent ( ) {
110
+ if let Some ( parent) = Path :: new ( filepath) . parent ( ) {
45
111
fs:: create_dir_all ( parent) ?;
46
112
}
47
113
@@ -52,7 +118,7 @@ fn main() -> io::Result<()> {
52
118
. write ( true )
53
119
. create ( true )
54
120
. truncate ( true )
55
- . open ( & args . filepath ) ?;
121
+ . open ( filepath) ?;
56
122
let mut writer = BufWriter :: new ( file) ;
57
123
58
124
for line in lines. iter ( ) {
@@ -65,7 +131,7 @@ fn main() -> io::Result<()> {
65
131
. append ( true )
66
132
. write ( true )
67
133
. create ( true )
68
- . open ( & args . filepath ) ?;
134
+ . open ( filepath) ?;
69
135
let mut writer = BufWriter :: new ( file) ;
70
136
71
137
for stdin_line in stdin. lock ( ) . lines ( ) {
@@ -88,23 +154,24 @@ fn main() -> io::Result<()> {
88
154
let mut sorted_lines: Vec < _ > = lines. into_iter ( ) . collect ( ) ;
89
155
sorted_lines. sort_by ( |a, b| natsort:: compare ( a, b, false ) ) ;
90
156
91
- let soet_file = OpenOptions :: new ( )
157
+ let sort_file = OpenOptions :: new ( )
92
158
. write ( true )
93
159
. create ( true )
94
160
. truncate ( true )
95
- . open ( & args . filepath ) ?;
96
- let mut soet_writer = BufWriter :: new ( soet_file ) ;
161
+ . open ( filepath) ?;
162
+ let mut sort_writer = BufWriter :: new ( sort_file ) ;
97
163
98
164
for line in sorted_lines. iter ( ) {
99
- writeln ! ( soet_writer , "{}" , line) ?;
165
+ writeln ! ( sort_writer , "{}" , line) ?;
100
166
}
101
167
}
102
168
103
169
Ok ( ( ) )
104
170
}
105
171
106
172
fn load_file ( args : & Options ) -> Result < IndexSet < String > , io:: Error > {
107
- match File :: open ( & args. filepath ) {
173
+ let filepath = args. filepath . as_ref ( ) . expect ( "Destination file is required" ) ;
174
+ match File :: open ( filepath) {
108
175
Ok ( file) => {
109
176
let reader = BufReader :: new ( file) ;
110
177
let mut lines = IndexSet :: new ( ) ;
@@ -126,3 +193,18 @@ fn should_add_line(args: &Options, lines: &IndexSet<String>, line: &str) -> bool
126
193
let trimmed_line = if args. trim { line. trim ( ) } else { line } ;
127
194
!trimmed_line. is_empty ( ) && !lines. contains ( trimmed_line)
128
195
}
196
+
197
+ // New helper function to load file content
198
+ fn load_file_content ( filepath : & str ) -> io:: Result < Vec < String > > {
199
+ match File :: open ( filepath) {
200
+ Ok ( file) => {
201
+ let reader = BufReader :: new ( file) ;
202
+ let mut lines = Vec :: new ( ) ;
203
+ for line in reader. lines ( ) {
204
+ lines. push ( line?) ;
205
+ }
206
+ Ok ( lines)
207
+ }
208
+ Err ( _) => Ok ( Vec :: new ( ) ) ,
209
+ }
210
+ }
0 commit comments