Skip to main content

id3_json/
input.rs

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