id3_json/
input.rs

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}