1use crate::{
2 app::{
3 field::{ArgsTable, Field, Text},
4 Run,
5 },
6 error::Error,
7 text_data::picture_type::PictureType,
8 utils::ModifyTags,
9};
10use clap::{Args, Subcommand};
11use id3::{Tag, TagLike};
12use std::{mem::replace, path::PathBuf};
13
14#[derive(Debug, Subcommand)]
16pub enum Delete {
17 All(DeleteAllField),
19 #[clap(flatten)]
21 Single(DeleteSingleField),
22}
23
24impl Run for Delete {
25 fn run(self) -> Result<(), Error> {
26 match self {
27 Delete::All(proc) => proc.run(),
28 Delete::Single(proc) => proc.run(),
29 }
30 }
31}
32
33#[derive(Debug, Args)]
35#[clap(about = "")]
36pub struct DeleteAllField {
37 #[clap(long)]
39 pub no_backup: bool,
40 pub target_audio: PathBuf,
42}
43
44impl Run for DeleteAllField {
45 fn run(self) -> Result<(), Error> {
46 let DeleteAllField {
47 no_backup,
48 target_audio,
49 } = self;
50 ModifyTags::builder()
51 .no_backup(no_backup)
52 .target_audio(&target_audio)
53 .build()
54 .run(|tag| replace(tag, Tag::new()))?;
55 Ok(())
56 }
57}
58
59pub type DeleteSingleField = Field<DeleteArgsTable>;
61
62impl Run for Text<DeleteArgsTable> {
63 fn run(self) -> Result<(), Error> {
64 fn delete_text(args: DeleteText, delete: impl FnOnce(&mut Tag)) -> Result<(), Error> {
65 let DeleteText {
66 no_backup,
67 target_audio,
68 } = args;
69 ModifyTags::builder()
70 .no_backup(no_backup)
71 .target_audio(&target_audio)
72 .build()
73 .run(delete)
74 }
75
76 match self {
77 Text::Title(args) => delete_text(args, Tag::remove_title),
78 Text::Artist(args) => delete_text(args, Tag::remove_artist),
79 Text::Album(args) => delete_text(args, Tag::remove_album),
80 Text::AlbumArtist(args) => delete_text(args, Tag::remove_album_artist),
81 Text::Genre(DeleteGenre::Genre(args)) => delete_text(args, Tag::remove_genre),
82 }
83 }
84}
85
86#[derive(Debug)]
88pub struct DeleteArgsTable;
89impl ArgsTable for DeleteArgsTable {
90 type Text = DeleteText;
91 type Genre = DeleteGenre;
92 type Comment = DeleteComment;
93 type Picture = DeletePicture;
94}
95
96#[derive(Debug, Args)]
98#[clap(about = "")]
99pub struct DeleteText {
100 #[clap(long)]
102 pub no_backup: bool,
103 pub target_audio: PathBuf,
105}
106
107#[derive(Debug, Subcommand)]
109pub enum DeleteGenre {
110 #[clap(name = "genre")]
111 Genre(DeleteText),
112}
113
114#[derive(Debug, Args)]
116#[clap(about = "")]
117pub struct DeleteComment {
118 #[clap(long)]
120 pub no_backup: bool,
121 #[clap(long)]
123 pub description: Option<String>,
124 #[clap(long)]
126 pub content: Option<String>,
127 pub target_audio: PathBuf,
129}
130
131impl Run for DeleteComment {
132 fn run(self) -> Result<(), Error> {
133 let DeleteComment {
134 no_backup,
135 ref description,
136 ref content,
137 ref target_audio,
138 } = self;
139 let description = description.as_deref();
140 let content = content.as_deref();
141 ModifyTags::builder()
142 .no_backup(no_backup)
143 .target_audio(target_audio)
144 .build()
145 .run(|tag| tag.remove_comment(description, content))
146 }
147}
148
149#[derive(Debug, Args)]
151#[clap(about = "")]
152pub struct DeletePicture {
153 #[clap(long)]
155 pub no_backup: bool,
156 pub target_audio: PathBuf,
158 #[clap(subcommand)]
160 pub command: DeletePictureCmd,
161}
162
163impl Run for DeletePicture {
164 fn run(self) -> Result<(), Error> {
165 let DeletePicture {
166 no_backup,
167 ref target_audio,
168 command,
169 } = self;
170
171 ModifyTags {
172 no_backup,
173 target_audio,
174 }
175 .run(|tag| match command {
176 DeletePictureCmd::All => tag.remove_all_pictures(),
177 DeletePictureCmd::ByType(picture_type) => {
178 tag.remove_picture_by_type(picture_type.into())
179 }
180 })
181 }
182}
183
184#[derive(Debug, Subcommand)]
186#[clap(about = "")]
187pub enum DeletePictureCmd {
188 All,
190 #[clap(flatten)]
192 ByType(PictureType),
193}