1use std::path::PathBuf;
2use std::ffi::OsString;
3
4use lexopt::prelude::*;
5
6#[derive(Debug, PartialEq, Eq)]
7pub struct Args {
8 pub filename: PathBuf,
9 pub read: bool,
10 pub write: bool,
11 pub tag_version: Option<id3::Version>,
12}
13
14pub fn parse_args<I>(args: I) -> Result<Args, lexopt::Error>
15where
16 I: IntoIterator + 'static,
17 I::Item: Into<OsString>,
18{
19 let mut filename_input = None;
20 let mut tag_version = None;
21 let mut read = false;
22 let mut write = false;
23 let mut parser = lexopt::Parser::from_iter(args);
24
25 while let Some(arg) = parser.next()? {
26 match arg {
27 Short('r') | Long("read") => read = true,
28 Short('w') | Long("write") => write = true,
29 Long("tag-version") => {
30 let mut input = parser.value()?;
31 input.make_ascii_lowercase();
32
33 if input == "id3v2.2" {
34 tag_version = Some(id3::Version::Id3v22);
35 } else if input == "id3v2.3" {
36 tag_version = Some(id3::Version::Id3v23);
37 } else if input == "id3v2.4" {
38 tag_version = Some(id3::Version::Id3v24);
39 } else {
40 let error = format!("Unsupported ID3 version: {:?}. Expected ID3v2.{{2,3,4}}", input);
41 return Err(lexopt::Error::Custom(error.into()));
42 }
43 },
44 Value(val) if filename_input.is_none() => {
45 filename_input = Some(PathBuf::from(val));
46 }
47 Short('V') | Long("version") => {
48 println!("id3-json {}", env!("CARGO_PKG_VERSION"));
49 std::process::exit(0);
50 }
51 Long("help") => {
52 print_help();
53 std::process::exit(0);
54 }
55 _ => return Err(arg.unexpected()),
56 }
57 }
58
59 let Some(filename) = filename_input else {
60 let error = String::from("Missing <filename.mp3>");
61 return Err(lexopt::Error::Custom(error.into()));
62 };
63
64 if !read && !write {
65 read = true;
66 }
67
68 Ok(Args { filename, read, write, tag_version })
69}
70
71fn print_help() {
72 println!("id3-json {}", env!("CARGO_PKG_VERSION"));
73 println!();
74 println!("USAGE:");
75 println!(" id3-json [FLAGS] <music-file.mp3>");
76 println!();
77 println!("FLAGS:");
78 println!(" -r, --read Reads tags from the file and outputs them to STDOUT as JSON.");
79 println!(" If neither `read` nor `write` are given, will read by default.");
80 println!();
81 println!(" -w, --write Write mode, expects a JSON on STDIN with valid tag values.");
82 println!(" If also given `read`, will print the resulting tags afterwards");
83 println!();
84 println!(" --tag-version <ID3v2.{{2,3,4}}>");
85 println!(" On write, sets the tags' version to 2.2, 2.3, or 2.4.");
86 println!();
87 println!(" -V, --version Prints version information");
88 println!();
89 println!("ARGS:");
90 println!(" <music-file.mp3> Music file to read tags from or write tags to");
91}