1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/// Options to control how Lofty writes to a file
///
/// This acts as a dumping ground for all sorts of format-specific settings. As such, this is best
/// used as an application global config that gets set once.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct WriteOptions {
	pub(crate) preferred_padding: Option<u32>,
	pub(crate) remove_others: bool,
	pub(crate) respect_read_only: bool,
	pub(crate) uppercase_id3v2_chunk: bool,
	pub(crate) use_id3v23: bool,
}

impl WriteOptions {
	/// Default preferred padding size in bytes
	pub const DEFAULT_PREFERRED_PADDING: u32 = 1024;

	/// Creates a new `WriteOptions`, alias for `Default` implementation
	///
	/// See also: [`WriteOptions::default`]
	///
	/// # Examples
	///
	/// ```rust
	/// use lofty::config::WriteOptions;
	///
	/// let write_options = WriteOptions::new();
	/// ```
	pub const fn new() -> Self {
		Self {
			preferred_padding: Some(Self::DEFAULT_PREFERRED_PADDING),
			remove_others: false,
			respect_read_only: true,
			uppercase_id3v2_chunk: true,
			use_id3v23: false,
		}
	}

	/// Set the preferred padding size in bytes
	///
	/// If the tag format being written supports padding, this will be the size of the padding
	/// in bytes.
	///
	/// NOTES:
	///
	/// * Not all tag formats support padding
	/// * The actual padding size may be different from this value, depending on tag size limitations
	///
	/// # Examples
	///
	/// ```rust
	/// use lofty::config::WriteOptions;
	///
	/// // I really don't want my files rewritten, so I'll double the padding size!
	/// let options = WriteOptions::new().preferred_padding(2048);
	///
	/// // ...Or I don't want padding under any circumstances!
	/// let options = WriteOptions::new().preferred_padding(0);
	/// ```
	pub fn preferred_padding(mut self, preferred_padding: u32) -> Self {
		match preferred_padding {
			0 => self.preferred_padding = None,
			_ => self.preferred_padding = Some(preferred_padding),
		}
		self
	}

	/// Whether to remove all other tags when writing
	///
	/// If set to `true`, only the tag being written will be kept in the file.
	///
	/// # Examples
	///
	/// ```rust,no_run
	/// use lofty::config::WriteOptions;
	/// use lofty::prelude::*;
	/// use lofty::tag::{Tag, TagType};
	///
	/// # fn main() -> lofty::error::Result<()> {
	/// let mut id3v2_tag = Tag::new(TagType::Id3v2);
	///
	/// // ...
	///
	/// // I only want to keep the ID3v2 tag around!
	/// let options = WriteOptions::new().remove_others(true);
	/// id3v2_tag.save_to_path("test.mp3", options)?;
	/// # Ok(()) }
	/// ```
	pub fn remove_others(mut self, remove_others: bool) -> Self {
		self.remove_others = remove_others;
		self
	}

	/// Whether to respect read-only tag items
	///
	/// Some tag formats allow for items to be marked as read-only. If set to `true`, these items
	/// will take priority over newly created tag items.
	///
	/// NOTE: In the case of APE tags, one can mark the entire tag as read-only. This will append
	/// the existing tag items to the new tag.
	///
	/// # Examples
	///
	/// ```rust,no_run
	/// use lofty::config::WriteOptions;
	/// use lofty::prelude::*;
	/// use lofty::tag::{Tag, TagType};
	///
	/// # fn main() -> lofty::error::Result<()> {
	/// let mut id3v2_tag = Tag::new(TagType::Id3v2);
	///
	/// // ...
	///
	/// // I don't care about read-only items, I want to write my new items!
	/// let options = WriteOptions::new().respect_read_only(false);
	/// id3v2_tag.save_to_path("test.mp3", options)?;
	/// # Ok(()) }
	/// ```
	pub fn respect_read_only(mut self, respect_read_only: bool) -> Self {
		self.respect_read_only = respect_read_only;
		self
	}

	/// Whether to uppercase the ID3v2 chunk name
	///
	/// When dealing with RIFF/AIFF files, some software may expect the ID3v2 chunk name to be
	/// lowercase.
	///
	/// NOTE: The vast majority of software will be able to read both upper and lowercase
	/// chunk names.
	///
	/// # Examples
	///
	/// ```rust,no_run
	/// use lofty::config::WriteOptions;
	/// use lofty::prelude::*;
	/// use lofty::tag::{Tag, TagType};
	///
	/// # fn main() -> lofty::error::Result<()> {
	/// let mut id3v2_tag = Tag::new(TagType::Id3v2);
	///
	/// // ...
	///
	/// // I want to keep the ID3v2 chunk name lowercase
	/// let options = WriteOptions::new().uppercase_id3v2_chunk(false);
	/// id3v2_tag.save_to_path("test.mp3", options)?;
	/// # Ok(()) }
	pub fn uppercase_id3v2_chunk(mut self, uppercase_id3v2_chunk: bool) -> Self {
		self.uppercase_id3v2_chunk = uppercase_id3v2_chunk;
		self
	}

	/// Whether or not to use ID3v2.3 when saving [`TagType::Id3v2`](crate::tag::TagType::Id3v2)
	/// or [`Id3v2Tag`](crate::id3::v2::Id3v2Tag)
	///
	/// By default, Lofty will save ID3v2.4 tags. This option allows you to save ID3v2.3 tags instead.
	///
	/// # Examples
	///
	/// ```rust,no_run
	/// use lofty::config::WriteOptions;
	/// use lofty::prelude::*;
	/// use lofty::tag::{Tag, TagType};
	///
	/// # fn main() -> lofty::error::Result<()> {
	/// let mut id3v2_tag = Tag::new(TagType::Id3v2);
	///
	/// // ...
	///
	/// // I need to save ID3v2.3 tags to support older software
	/// let options = WriteOptions::new().use_id3v23(true);
	/// id3v2_tag.save_to_path("test.mp3", options)?;
	/// # Ok(()) }
	/// ```
	pub fn use_id3v23(&mut self, use_id3v23: bool) -> Self {
		self.use_id3v23 = use_id3v23;
		*self
	}
}

impl Default for WriteOptions {
	/// The default implementation for `WriteOptions`
	///
	/// The defaults are as follows:
	///
	/// ```rust,ignore
	/// WriteOptions {
	///     preferred_padding: 1024,
	///     remove_others: false,
	///     respect_read_only: true,
	///     uppercase_id3v2_chunk: true,
	///     use_id3v23: false,
	/// }
	/// ```
	fn default() -> Self {
		Self::new()
	}
}