anni_flac/blocks/
comment.rs

1use crate::prelude::*;
2use crate::utils::*;
3use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
4use std::collections::HashMap;
5use std::fmt;
6use std::fmt::Display;
7use std::io::{Read, Write};
8
9/// Also known as FLAC tags, the contents of a vorbis comment packet as specified here (without the framing bit).
10/// Note that the vorbis comment spec allows for on the order of 2 ^ 64 bytes of data where as the FLAC metadata block is limited to 2 ^ 24 bytes.
11/// Given the stated purpose of vorbis comments, i.e. human-readable textual information, this limit is unlikely to be restrictive.
12/// Also note that the 32-bit field lengths are **little-endian** coded according to the vorbis spec, as opposed to the usual big-endian coding of fixed-length integers in the rest of FLAC.
13///
14/// The Vorbis text comment header is the second (of three) header packets that begin a Vorbis bitstream.
15/// It is meant for short, text comments, not arbitrary metadata; arbitrary metadata belongs in a separate logical bitstream (usually an XML stream type) that provides greater structure and machine parseability.
16///
17/// The comment field is meant to be used much like someone jotting a quick note on the bottom of a CDR.
18/// It should be a little information to remember the disc by and explain it to others; a short, to-the-point text note that need not only be a couple words, but isn't going to be more than a short paragraph.
19///
20/// The essentials, in other words, whatever they turn out to be, eg:
21///     "Honest Bob and the Factory-to-Dealer-Incentives, _I'm Still Around_, opening for Moxy Früvous, 1997"
22pub struct BlockVorbisComment {
23    // [vendor_length] = read an unsigned integer of 32 bits
24    // vendor_length: u32,
25    /// [vendor_string] = read a UTF-8 vector as [vendor_length] octets
26    pub vendor_string: String,
27
28    // [user_comment_list_length] = read an unsigned integer of 32 bits
29    // comment_number: u32,
30    /// iterate [user_comment_list_length] times
31    pub comments: Vec<UserComment>,
32    // [framing_bit] = read a single bit as boolean
33    // if ( [framing_bit] unset or end of packet ) then ERROR
34}
35
36impl BlockVorbisComment {
37    pub fn push(&mut self, comment: UserComment) {
38        self.comments.push(comment);
39    }
40
41    #[inline]
42    pub fn len(&self) -> usize {
43        self.comments.len()
44    }
45
46    #[inline]
47    pub fn is_empty(&self) -> bool {
48        self.len() == 0
49    }
50
51    pub fn clear(&mut self) {
52        self.comments.clear()
53    }
54
55    pub fn to_map(&self) -> HashMap<String, &UserComment> {
56        let mut map: HashMap<_, _> = Default::default();
57        for comment in self.comments.iter() {
58            // NOT override only when key exists AND comment.value is EMPTY.
59            if !(map.contains_key(&comment.key()) && comment.value().is_empty()) {
60                map.insert(comment.key(), comment);
61            }
62        }
63        map
64    }
65}
66
67impl Decode for BlockVorbisComment {
68    fn from_reader<R: Read>(reader: &mut R) -> Result<Self> {
69        let vendor_length = reader.read_u32::<LittleEndian>()?;
70        let vendor_string = take_string(reader, vendor_length as usize)?;
71        let comment_number = reader.read_u32::<LittleEndian>()?;
72        let mut comments = Vec::with_capacity(comment_number as usize);
73
74        for _ in 0..comment_number {
75            comments.push(UserComment::from_reader(reader)?);
76        }
77
78        Ok(BlockVorbisComment {
79            vendor_string,
80            comments,
81        })
82    }
83}
84
85#[cfg(feature = "async")]
86#[async_trait::async_trait]
87impl AsyncDecode for BlockVorbisComment {
88    async fn from_async_reader<R>(reader: &mut R) -> Result<Self>
89    where
90        R: AsyncRead + Unpin + Send,
91    {
92        let vendor_length = reader.read_u32_le().await?;
93        let vendor_string = take_string_async(reader, vendor_length as usize).await?;
94        let comment_number = reader.read_u32_le().await?;
95        let mut comments = Vec::with_capacity(comment_number as usize);
96
97        for _ in 0..comment_number {
98            comments.push(UserComment::from_async_reader(reader).await?);
99        }
100
101        Ok(BlockVorbisComment {
102            vendor_string,
103            comments,
104        })
105    }
106}
107
108impl Encode for BlockVorbisComment {
109    fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
110        writer.write_u32::<LittleEndian>(self.vendor_string.len() as u32)?;
111        writer.write_all(self.vendor_string.as_bytes())?;
112        writer.write_u32::<LittleEndian>(self.comments.len() as u32)?;
113        for comment in self.comments.iter() {
114            comment.write_to(writer)?;
115        }
116        Ok(())
117    }
118}
119
120impl fmt::Debug for BlockVorbisComment {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        let mut prefix = "".to_owned();
123        if let Some(width) = f.width() {
124            prefix = " ".repeat(width);
125        }
126        writeln!(
127            f,
128            "{prefix}vendor string: {}",
129            self.vendor_string,
130            prefix = prefix
131        )?;
132        writeln!(f, "{prefix}comments: {}", self.len(), prefix = prefix)?;
133        for (i, c) in self.comments.iter().enumerate() {
134            write!(f, "{}", prefix)?;
135            writeln!(
136                f,
137                "{prefix}comment[{}]: {}={}",
138                i,
139                c.key_raw(),
140                c.value(),
141                prefix = prefix
142            )?;
143        }
144        Ok(())
145    }
146}
147
148impl fmt::Display for BlockVorbisComment {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        for c in self.comments.iter() {
151            writeln!(f, "{}", c.comment)?;
152        }
153        Ok(())
154    }
155}
156
157#[derive(Debug)]
158pub struct UserComment {
159    // [length] = read an unsigned integer of 32 bits
160    // length: u32,
161    /// this iteration's user comment = read a UTF-8 vector as [length] octets
162    comment: String,
163    value_offset: Option<usize>,
164}
165
166impl UserComment {
167    pub fn new(comment: String) -> Self {
168        let value_offset = comment.find('=');
169        Self {
170            comment,
171            value_offset,
172        }
173    }
174
175    pub fn key(&self) -> String {
176        self.key_raw().to_ascii_uppercase()
177    }
178
179    pub fn key_raw(&self) -> &str {
180        match self.value_offset {
181            Some(offset) => &self.comment[..offset],
182            None => &self.comment,
183        }
184    }
185
186    pub fn is_key_uppercase(&self) -> bool {
187        let key = match self.value_offset {
188            Some(offset) => &self.comment[..offset],
189            None => &self.comment,
190        };
191
192        key.chars().all(|c| !c.is_ascii_lowercase())
193    }
194
195    pub fn value(&self) -> &str {
196        match self.value_offset {
197            Some(offset) => &self.comment[offset + 1..],
198            None => &self.comment[self.comment.len()..],
199        }
200    }
201
202    pub fn len(&self) -> usize {
203        self.comment.len()
204    }
205
206    pub fn entry(&self) -> String {
207        self.comment.clone()
208    }
209
210    pub fn is_empty(&self) -> bool {
211        self.comment.is_empty()
212    }
213
214    pub fn clear(&mut self) {
215        self.comment.clear();
216        self.value_offset = None;
217    }
218}
219
220impl Decode for UserComment {
221    fn from_reader<R: Read>(reader: &mut R) -> Result<Self> {
222        let length = reader.read_u32::<LittleEndian>()?;
223        let comment = take_string(reader, length as usize)?;
224        Ok(UserComment::new(comment))
225    }
226}
227
228#[cfg(feature = "async")]
229#[async_trait::async_trait]
230impl AsyncDecode for UserComment {
231    async fn from_async_reader<R>(reader: &mut R) -> Result<Self>
232    where
233        R: AsyncRead + Unpin + Send,
234    {
235        let length = reader.read_u32_le().await?;
236        let comment = take_string_async(reader, length as usize).await?;
237        Ok(UserComment::new(comment))
238    }
239}
240
241impl Encode for UserComment {
242    fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
243        writer.write_u32::<LittleEndian>(self.comment.len() as u32)?;
244        writer.write_all(self.comment.as_bytes())?;
245        Ok(())
246    }
247}
248
249pub trait UserCommentExt {
250    fn title<S>(value: S) -> Self
251    where
252        S: Display;
253    fn artist<S>(value: S) -> Self
254    where
255        S: Display;
256    fn album<S>(value: S) -> Self
257    where
258        S: Display;
259    fn date<S>(value: S) -> Self
260    where
261        S: Display;
262    fn track_number<S>(value: S) -> Self
263    where
264        S: Display;
265    fn track_total<S>(value: S) -> Self
266    where
267        S: Display;
268    fn disc_number<S>(value: S) -> Self
269    where
270        S: Display;
271    fn disc_total<S>(value: S) -> Self
272    where
273        S: Display;
274    fn album_artist<S>(value: S) -> Self
275    where
276        S: Display;
277}
278
279impl UserCommentExt for UserComment {
280    fn title<S>(value: S) -> Self
281    where
282        S: Display,
283    {
284        Self::new(format!("TITLE={}", value))
285    }
286
287    fn artist<S>(value: S) -> Self
288    where
289        S: Display,
290    {
291        Self::new(format!("ARTIST={}", value))
292    }
293
294    fn album<S>(value: S) -> Self
295    where
296        S: Display,
297    {
298        Self::new(format!("ALBUM={}", value))
299    }
300
301    fn date<S>(value: S) -> Self
302    where
303        S: Display,
304    {
305        Self::new(format!("DATE={}", value))
306    }
307
308    fn track_number<S>(value: S) -> Self
309    where
310        S: Display,
311    {
312        Self::new(format!("TRACKNUMBER={}", value))
313    }
314
315    fn track_total<S>(value: S) -> Self
316    where
317        S: Display,
318    {
319        Self::new(format!("TRACKTOTAL={}", value))
320    }
321
322    fn disc_number<S>(value: S) -> Self
323    where
324        S: Display,
325    {
326        Self::new(format!("DISCNUMBER={}", value))
327    }
328
329    fn disc_total<S>(value: S) -> Self
330    where
331        S: Display,
332    {
333        Self::new(format!("DISCTOTAL={}", value))
334    }
335
336    fn album_artist<S>(value: S) -> Self
337    where
338        S: Display,
339    {
340        Self::new(format!("ALBUMARTIST={}", value))
341    }
342}