+462
Cargo.lock
+462
Cargo.lock
···
1
+
# This file is automatically @generated by Cargo.
2
+
# It is not intended for manual editing.
3
+
version = 4
4
+
5
+
[[package]]
6
+
name = "adler2"
7
+
version = "2.0.1"
8
+
source = "registry+https://github.com/rust-lang/crates.io-index"
9
+
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
10
+
11
+
[[package]]
12
+
name = "anstream"
13
+
version = "0.6.20"
14
+
source = "registry+https://github.com/rust-lang/crates.io-index"
15
+
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
16
+
dependencies = [
17
+
"anstyle",
18
+
"anstyle-parse",
19
+
"anstyle-query",
20
+
"anstyle-wincon",
21
+
"colorchoice",
22
+
"is_terminal_polyfill",
23
+
"utf8parse",
24
+
]
25
+
26
+
[[package]]
27
+
name = "anstyle"
28
+
version = "1.0.11"
29
+
source = "registry+https://github.com/rust-lang/crates.io-index"
30
+
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
31
+
32
+
[[package]]
33
+
name = "anstyle-parse"
34
+
version = "0.2.7"
35
+
source = "registry+https://github.com/rust-lang/crates.io-index"
36
+
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
37
+
dependencies = [
38
+
"utf8parse",
39
+
]
40
+
41
+
[[package]]
42
+
name = "anstyle-query"
43
+
version = "1.1.4"
44
+
source = "registry+https://github.com/rust-lang/crates.io-index"
45
+
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
46
+
dependencies = [
47
+
"windows-sys",
48
+
]
49
+
50
+
[[package]]
51
+
name = "anstyle-wincon"
52
+
version = "3.0.10"
53
+
source = "registry+https://github.com/rust-lang/crates.io-index"
54
+
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
55
+
dependencies = [
56
+
"anstyle",
57
+
"once_cell_polyfill",
58
+
"windows-sys",
59
+
]
60
+
61
+
[[package]]
62
+
name = "audiotags"
63
+
version = "0.5.0"
64
+
source = "registry+https://github.com/rust-lang/crates.io-index"
65
+
checksum = "44e797ce0164cf599c71f2c3849b56301d96a3dc033544588e875686b050ed39"
66
+
dependencies = [
67
+
"audiotags-macro",
68
+
"id3",
69
+
"metaflac",
70
+
"mp4ameta",
71
+
"readme-rustdocifier",
72
+
"thiserror",
73
+
]
74
+
75
+
[[package]]
76
+
name = "audiotags-macro"
77
+
version = "0.2.0"
78
+
source = "registry+https://github.com/rust-lang/crates.io-index"
79
+
checksum = "8eaa9b2312fc01f7291f3b7b0f52ed08b1c0177c96a2e696ab55695cc4d06889"
80
+
81
+
[[package]]
82
+
name = "bitflags"
83
+
version = "2.9.4"
84
+
source = "registry+https://github.com/rust-lang/crates.io-index"
85
+
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
86
+
87
+
[[package]]
88
+
name = "byteorder"
89
+
version = "1.5.0"
90
+
source = "registry+https://github.com/rust-lang/crates.io-index"
91
+
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
92
+
93
+
[[package]]
94
+
name = "cfg-if"
95
+
version = "1.0.3"
96
+
source = "registry+https://github.com/rust-lang/crates.io-index"
97
+
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
98
+
99
+
[[package]]
100
+
name = "clap"
101
+
version = "4.5.48"
102
+
source = "registry+https://github.com/rust-lang/crates.io-index"
103
+
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
104
+
dependencies = [
105
+
"clap_builder",
106
+
"clap_derive",
107
+
]
108
+
109
+
[[package]]
110
+
name = "clap_builder"
111
+
version = "4.5.48"
112
+
source = "registry+https://github.com/rust-lang/crates.io-index"
113
+
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
114
+
dependencies = [
115
+
"anstream",
116
+
"anstyle",
117
+
"clap_lex",
118
+
"strsim",
119
+
]
120
+
121
+
[[package]]
122
+
name = "clap_derive"
123
+
version = "4.5.47"
124
+
source = "registry+https://github.com/rust-lang/crates.io-index"
125
+
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
126
+
dependencies = [
127
+
"heck",
128
+
"proc-macro2",
129
+
"quote",
130
+
"syn",
131
+
]
132
+
133
+
[[package]]
134
+
name = "clap_lex"
135
+
version = "0.7.5"
136
+
source = "registry+https://github.com/rust-lang/crates.io-index"
137
+
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
138
+
139
+
[[package]]
140
+
name = "colorchoice"
141
+
version = "1.0.4"
142
+
source = "registry+https://github.com/rust-lang/crates.io-index"
143
+
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
144
+
145
+
[[package]]
146
+
name = "crc32fast"
147
+
version = "1.5.0"
148
+
source = "registry+https://github.com/rust-lang/crates.io-index"
149
+
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
150
+
dependencies = [
151
+
"cfg-if",
152
+
]
153
+
154
+
[[package]]
155
+
name = "flate2"
156
+
version = "1.1.2"
157
+
source = "registry+https://github.com/rust-lang/crates.io-index"
158
+
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
159
+
dependencies = [
160
+
"crc32fast",
161
+
"miniz_oxide",
162
+
]
163
+
164
+
[[package]]
165
+
name = "heck"
166
+
version = "0.5.0"
167
+
source = "registry+https://github.com/rust-lang/crates.io-index"
168
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
169
+
170
+
[[package]]
171
+
name = "hex"
172
+
version = "0.4.3"
173
+
source = "registry+https://github.com/rust-lang/crates.io-index"
174
+
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
175
+
176
+
[[package]]
177
+
name = "id3"
178
+
version = "1.16.3"
179
+
source = "registry+https://github.com/rust-lang/crates.io-index"
180
+
checksum = "aadb14a5ba1a0d58ecd4a29bfc9b8f1d119eee24aa01a62c1ec93eb9630a1d86"
181
+
dependencies = [
182
+
"bitflags",
183
+
"byteorder",
184
+
"flate2",
185
+
]
186
+
187
+
[[package]]
188
+
name = "is_terminal_polyfill"
189
+
version = "1.70.1"
190
+
source = "registry+https://github.com/rust-lang/crates.io-index"
191
+
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
192
+
193
+
[[package]]
194
+
name = "itoa"
195
+
version = "1.0.15"
196
+
source = "registry+https://github.com/rust-lang/crates.io-index"
197
+
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
198
+
199
+
[[package]]
200
+
name = "lazy_static"
201
+
version = "1.5.0"
202
+
source = "registry+https://github.com/rust-lang/crates.io-index"
203
+
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
204
+
205
+
[[package]]
206
+
name = "memchr"
207
+
version = "2.7.5"
208
+
source = "registry+https://github.com/rust-lang/crates.io-index"
209
+
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
210
+
211
+
[[package]]
212
+
name = "metaflac"
213
+
version = "0.2.8"
214
+
source = "registry+https://github.com/rust-lang/crates.io-index"
215
+
checksum = "fdf25a3451319c52a4a56d956475fbbb763bfb8420e2187d802485cb0fd8d965"
216
+
dependencies = [
217
+
"byteorder",
218
+
"hex",
219
+
]
220
+
221
+
[[package]]
222
+
name = "miniz_oxide"
223
+
version = "0.8.9"
224
+
source = "registry+https://github.com/rust-lang/crates.io-index"
225
+
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
226
+
dependencies = [
227
+
"adler2",
228
+
]
229
+
230
+
[[package]]
231
+
name = "mp4ameta"
232
+
version = "0.11.0"
233
+
source = "registry+https://github.com/rust-lang/crates.io-index"
234
+
checksum = "eb23d62e8eb5299a3f79657c70ea9269eac8f6239a76952689bcd06a74057e81"
235
+
dependencies = [
236
+
"lazy_static",
237
+
"mp4ameta_proc",
238
+
]
239
+
240
+
[[package]]
241
+
name = "mp4ameta_proc"
242
+
version = "0.6.0"
243
+
source = "registry+https://github.com/rust-lang/crates.io-index"
244
+
checksum = "07dcca13d1740c0a665f77104803360da0bdb3323ecce2e93fa2c959a6d52806"
245
+
246
+
[[package]]
247
+
name = "once_cell_polyfill"
248
+
version = "1.70.1"
249
+
source = "registry+https://github.com/rust-lang/crates.io-index"
250
+
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
251
+
252
+
[[package]]
253
+
name = "proc-macro2"
254
+
version = "1.0.101"
255
+
source = "registry+https://github.com/rust-lang/crates.io-index"
256
+
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
257
+
dependencies = [
258
+
"unicode-ident",
259
+
]
260
+
261
+
[[package]]
262
+
name = "quote"
263
+
version = "1.0.40"
264
+
source = "registry+https://github.com/rust-lang/crates.io-index"
265
+
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
266
+
dependencies = [
267
+
"proc-macro2",
268
+
]
269
+
270
+
[[package]]
271
+
name = "readme-rustdocifier"
272
+
version = "0.1.1"
273
+
source = "registry+https://github.com/rust-lang/crates.io-index"
274
+
checksum = "08ad765b21a08b1a8e5cdce052719188a23772bcbefb3c439f0baaf62c56ceac"
275
+
276
+
[[package]]
277
+
name = "ryu"
278
+
version = "1.0.20"
279
+
source = "registry+https://github.com/rust-lang/crates.io-index"
280
+
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
281
+
282
+
[[package]]
283
+
name = "seekertools"
284
+
version = "0.1.0"
285
+
dependencies = [
286
+
"audiotags",
287
+
"clap",
288
+
"serde",
289
+
"serde_json",
290
+
]
291
+
292
+
[[package]]
293
+
name = "serde"
294
+
version = "1.0.226"
295
+
source = "registry+https://github.com/rust-lang/crates.io-index"
296
+
checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
297
+
dependencies = [
298
+
"serde_core",
299
+
"serde_derive",
300
+
]
301
+
302
+
[[package]]
303
+
name = "serde_core"
304
+
version = "1.0.226"
305
+
source = "registry+https://github.com/rust-lang/crates.io-index"
306
+
checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
307
+
dependencies = [
308
+
"serde_derive",
309
+
]
310
+
311
+
[[package]]
312
+
name = "serde_derive"
313
+
version = "1.0.226"
314
+
source = "registry+https://github.com/rust-lang/crates.io-index"
315
+
checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
316
+
dependencies = [
317
+
"proc-macro2",
318
+
"quote",
319
+
"syn",
320
+
]
321
+
322
+
[[package]]
323
+
name = "serde_json"
324
+
version = "1.0.145"
325
+
source = "registry+https://github.com/rust-lang/crates.io-index"
326
+
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
327
+
dependencies = [
328
+
"itoa",
329
+
"memchr",
330
+
"ryu",
331
+
"serde",
332
+
"serde_core",
333
+
]
334
+
335
+
[[package]]
336
+
name = "strsim"
337
+
version = "0.11.1"
338
+
source = "registry+https://github.com/rust-lang/crates.io-index"
339
+
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
340
+
341
+
[[package]]
342
+
name = "syn"
343
+
version = "2.0.106"
344
+
source = "registry+https://github.com/rust-lang/crates.io-index"
345
+
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
346
+
dependencies = [
347
+
"proc-macro2",
348
+
"quote",
349
+
"unicode-ident",
350
+
]
351
+
352
+
[[package]]
353
+
name = "thiserror"
354
+
version = "1.0.69"
355
+
source = "registry+https://github.com/rust-lang/crates.io-index"
356
+
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
357
+
dependencies = [
358
+
"thiserror-impl",
359
+
]
360
+
361
+
[[package]]
362
+
name = "thiserror-impl"
363
+
version = "1.0.69"
364
+
source = "registry+https://github.com/rust-lang/crates.io-index"
365
+
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
366
+
dependencies = [
367
+
"proc-macro2",
368
+
"quote",
369
+
"syn",
370
+
]
371
+
372
+
[[package]]
373
+
name = "unicode-ident"
374
+
version = "1.0.19"
375
+
source = "registry+https://github.com/rust-lang/crates.io-index"
376
+
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
377
+
378
+
[[package]]
379
+
name = "utf8parse"
380
+
version = "0.2.2"
381
+
source = "registry+https://github.com/rust-lang/crates.io-index"
382
+
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
383
+
384
+
[[package]]
385
+
name = "windows-link"
386
+
version = "0.1.3"
387
+
source = "registry+https://github.com/rust-lang/crates.io-index"
388
+
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
389
+
390
+
[[package]]
391
+
name = "windows-sys"
392
+
version = "0.60.2"
393
+
source = "registry+https://github.com/rust-lang/crates.io-index"
394
+
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
395
+
dependencies = [
396
+
"windows-targets",
397
+
]
398
+
399
+
[[package]]
400
+
name = "windows-targets"
401
+
version = "0.53.3"
402
+
source = "registry+https://github.com/rust-lang/crates.io-index"
403
+
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
404
+
dependencies = [
405
+
"windows-link",
406
+
"windows_aarch64_gnullvm",
407
+
"windows_aarch64_msvc",
408
+
"windows_i686_gnu",
409
+
"windows_i686_gnullvm",
410
+
"windows_i686_msvc",
411
+
"windows_x86_64_gnu",
412
+
"windows_x86_64_gnullvm",
413
+
"windows_x86_64_msvc",
414
+
]
415
+
416
+
[[package]]
417
+
name = "windows_aarch64_gnullvm"
418
+
version = "0.53.0"
419
+
source = "registry+https://github.com/rust-lang/crates.io-index"
420
+
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
421
+
422
+
[[package]]
423
+
name = "windows_aarch64_msvc"
424
+
version = "0.53.0"
425
+
source = "registry+https://github.com/rust-lang/crates.io-index"
426
+
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
427
+
428
+
[[package]]
429
+
name = "windows_i686_gnu"
430
+
version = "0.53.0"
431
+
source = "registry+https://github.com/rust-lang/crates.io-index"
432
+
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
433
+
434
+
[[package]]
435
+
name = "windows_i686_gnullvm"
436
+
version = "0.53.0"
437
+
source = "registry+https://github.com/rust-lang/crates.io-index"
438
+
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
439
+
440
+
[[package]]
441
+
name = "windows_i686_msvc"
442
+
version = "0.53.0"
443
+
source = "registry+https://github.com/rust-lang/crates.io-index"
444
+
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
445
+
446
+
[[package]]
447
+
name = "windows_x86_64_gnu"
448
+
version = "0.53.0"
449
+
source = "registry+https://github.com/rust-lang/crates.io-index"
450
+
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
451
+
452
+
[[package]]
453
+
name = "windows_x86_64_gnullvm"
454
+
version = "0.53.0"
455
+
source = "registry+https://github.com/rust-lang/crates.io-index"
456
+
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
457
+
458
+
[[package]]
459
+
name = "windows_x86_64_msvc"
460
+
version = "0.53.0"
461
+
source = "registry+https://github.com/rust-lang/crates.io-index"
462
+
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+10
Cargo.toml
+10
Cargo.toml
+240
src/main.rs
+240
src/main.rs
···
1
+
use std::{fs, path::{Path, PathBuf}};
2
+
3
+
use audiotags::{AudioTag, Tag};
4
+
use clap::Parser;
5
+
6
+
/// Simple audio tag reader
7
+
#[derive(Parser, Debug)]
8
+
#[command(author, version, about)]
9
+
struct Args {
10
+
/// Read files recursively
11
+
#[arg(short, long)]
12
+
recursive: bool,
13
+
14
+
/// Output format: one of json, print, debug
15
+
/// Output format: one of json, pjson, print, debug
16
+
#[arg(short = 'o', long, default_value_t = Output::Json)]
17
+
output: Output,
18
+
19
+
/// Pretty-print output. With `--output json` this prints pretty JSON. With `--output debug` this prints human-friendly text.
20
+
#[arg(long)]
21
+
pretty: bool,
22
+
/// Directory to scan
23
+
directory: String,
24
+
}
25
+
26
+
#[derive(clap::ValueEnum, Clone, Debug)]
27
+
enum Output {
28
+
Json,
29
+
Debug,
30
+
}
31
+
32
+
impl std::fmt::Display for Output {
33
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34
+
match self {
35
+
Output::Json => write!(f, "json"),
36
+
Output::Debug => write!(f, "debug"),
37
+
}
38
+
}
39
+
}
40
+
41
+
#[derive(serde::Serialize)]
42
+
struct AudioFileSummary {
43
+
path: PathBuf,
44
+
title: String,
45
+
artist: String,
46
+
album: Option<String>,
47
+
cover_present: bool,
48
+
}
49
+
50
+
impl AudioFileEntry {
51
+
fn to_summary(&self) -> AudioFileSummary {
52
+
let title = self.tags.title().unwrap_or("Unknown").to_string();
53
+
let artist = self.tags.artist().unwrap_or("Unknown").to_string();
54
+
55
+
let (album, cover_present) = match self.tags.album() {
56
+
Some(a) => (Some(a.title.to_string()), a.cover.is_some()),
57
+
None => (None, false),
58
+
};
59
+
60
+
AudioFileSummary {
61
+
path: self.path.clone(),
62
+
title,
63
+
artist,
64
+
album,
65
+
cover_present,
66
+
}
67
+
}
68
+
}
69
+
70
+
pub struct AudioFileEntry {
71
+
pub path: PathBuf,
72
+
pub tags: Box<dyn AudioTag>,
73
+
}
74
+
75
+
impl std::fmt::Debug for AudioFileEntry {
76
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77
+
let cover_present = match self.tags.album() {
78
+
Some(album) => album.cover.is_some(),
79
+
None => false,
80
+
};
81
+
82
+
let album_info = self.tags.album().as_ref().map(|a| format!("{} by {}", a.title, a.artist.as_deref().unwrap_or("Unknown Artist")));
83
+
84
+
f.debug_struct("AudioFileEntry")
85
+
.field("path", &self.path)
86
+
.field("title", &self.tags.title().unwrap_or("Unknown"))
87
+
.field("artist", &self.tags.artist().unwrap_or("Unknown"))
88
+
.field("album", &album_info)
89
+
.field("cover_present", &cover_present)
90
+
.finish()
91
+
}
92
+
}
93
+
94
+
impl std::fmt::Display for AudioFileEntry {
95
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96
+
let title = self.tags.title().unwrap_or("Unknown");
97
+
let artist = self.tags.artist().unwrap_or("Unknown");
98
+
let (album_info, cover_present) = match self.tags.album() {
99
+
Some(album) => {
100
+
let artist = album.artist.as_ref().map(|s| &**s).unwrap_or("Unknown Artist");
101
+
let info = format!("{} by {}", album.title, artist);
102
+
(info, album.cover.is_some())
103
+
}
104
+
None => ("No album".to_string(), false),
105
+
};
106
+
107
+
write!(f, "{} | {} | {} | {} | cover_present: {}", self.path.display(), title, artist, album_info, cover_present)
108
+
}
109
+
}
110
+
111
+
type ResultType = Result<Vec<AudioFileEntry>, Box<dyn std::error::Error>>;
112
+
113
+
fn is_audio_file(path: &Path) -> bool {
114
+
if let Some(extension) = path.extension().and_then(|s| s.to_str()) {
115
+
matches!(
116
+
extension.to_lowercase().as_str(),
117
+
"mp3" | "flac" | "ogg" | "wav" | "m4a" | "aac"
118
+
)
119
+
} else {
120
+
false
121
+
}
122
+
}
123
+
124
+
fn read_folder<P: AsRef<Path>>(path: P, recursive: bool) -> ResultType {
125
+
let dir_path = path.as_ref();
126
+
if !dir_path.is_dir() {
127
+
return Err(format!("Path is not a directory: {}", dir_path.display()).into());
128
+
}
129
+
130
+
let mut audio_files = Vec::new();
131
+
132
+
// Use a stack for iterative traversal so we can do recursive or single-level scanning
133
+
let mut dirs = vec![dir_path.to_path_buf()];
134
+
135
+
while let Some(current_dir) = dirs.pop() {
136
+
let entries = match fs::read_dir(¤t_dir) {
137
+
Ok(e) => e,
138
+
Err(err) => {
139
+
eprintln!("Failed to read directory {}: {}", current_dir.display(), err);
140
+
continue;
141
+
}
142
+
};
143
+
144
+
for entry_res in entries {
145
+
let entry = match entry_res {
146
+
Ok(e) => e,
147
+
Err(e) => {
148
+
eprintln!("Failed to read dir entry in {}: {}", current_dir.display(), e);
149
+
continue;
150
+
}
151
+
};
152
+
153
+
let file_path = entry.path();
154
+
155
+
if file_path.is_dir() {
156
+
if recursive {
157
+
dirs.push(file_path);
158
+
}
159
+
continue;
160
+
}
161
+
162
+
if !(file_path.is_file() && is_audio_file(&file_path)) {
163
+
continue;
164
+
}
165
+
166
+
match Tag::new().read_from_path(&file_path) {
167
+
Ok(tags) => {
168
+
// store the path relative to the provided root directory
169
+
let rel_path = if let Ok(rel) = file_path.strip_prefix(dir_path) {
170
+
rel.to_path_buf()
171
+
} else if let Some(name) = file_path.file_name() {
172
+
PathBuf::from(name)
173
+
} else {
174
+
// fallback to the full path if all else fails
175
+
file_path.clone()
176
+
};
177
+
178
+
audio_files.push(AudioFileEntry {
179
+
path: rel_path,
180
+
tags,
181
+
});
182
+
}
183
+
Err(e) => {
184
+
eprintln!("Failed to read tags from {}: {:?}", file_path.display(), e);
185
+
// Continue processing other files instead of failing entirely
186
+
}
187
+
}
188
+
}
189
+
}
190
+
191
+
Ok(audio_files)
192
+
}
193
+
194
+
195
+
fn main() {
196
+
let args = Args::parse();
197
+
198
+
match read_folder(&args.directory, args.recursive) {
199
+
Ok(audio_files) => {
200
+
println!("Found {} audio files in '{}':", audio_files.len(), args.directory);
201
+
// Decide behavior based on `--pretty` and `--output`
202
+
if args.pretty {
203
+
match args.output {
204
+
Output::Json => {
205
+
// pretty JSON array
206
+
let summaries: Vec<_> = audio_files.into_iter().map(|e| e.to_summary()).collect();
207
+
match serde_json::to_string_pretty(&summaries) {
208
+
Ok(s) => println!("{}", s),
209
+
Err(e) => eprintln!("Failed to serialize summaries to pretty JSON: {}", e),
210
+
}
211
+
}
212
+
Output::Debug => {
213
+
// pretty + debug => human-friendly printed lines
214
+
for entry in audio_files {
215
+
println!("{}", entry);
216
+
}
217
+
}
218
+
}
219
+
} else {
220
+
// not pretty: per-entry outputs
221
+
for entry in audio_files {
222
+
match args.output {
223
+
Output::Debug => println!("{:?}", entry),
224
+
Output::Json => {
225
+
let summary = entry.to_summary();
226
+
match serde_json::to_string(&summary) {
227
+
Ok(s) => println!("{}", s),
228
+
Err(e) => eprintln!("Failed to serialize JSON for {}: {}", entry.path.display(), e),
229
+
}
230
+
}
231
+
}
232
+
}
233
+
}
234
+
}
235
+
Err(e) => {
236
+
eprintln!("Error reading folder '{}': {}", args.directory, e);
237
+
std::process::exit(1);
238
+
}
239
+
}
240
+
}