flac_codec/metadata/
mod.rs

1// Copyright 2025 Brian Langenberger
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! For handling a FLAC file's metadata blocks
10//!
11//! Many items are capitalized simply because they were capitalized
12//! in the original FLAC format documentation.
13//!
14//! # Metadata Blocks
15//!
16//! FLAC supports seven different metadata block types
17//!
18//! | Block Type | Purpose |
19//! |-----------:|---------|
20//! | [STREAMINFO](`Streaminfo`) | stream information such as sample rate, channel count, etc. |
21//! | [PADDING](`Padding`) | empty data which can easily be resized as needed |
22//! | [APPLICATION](`Application`) | application-specific data such as foreign RIFF WAVE chunks |
23//! | [SEEKTABLE](`SeekTable`) | to allow for more efficient seeking within a FLAC file |
24//! | [VORBIS_COMMENT](`VorbisComment`) | textual metadata such as track title, artist name, album name, etc. |
25//! | [CUESHEET](`Cuesheet`) | the original disc's layout, for CD images |
26//! | [PICTURE](`Picture`) | embedded image files such as cover art |
27
28use crate::Error;
29use bitstream_io::{
30    BigEndian, BitRead, BitReader, BitWrite, ByteRead, ByteReader, FromBitStream,
31    FromBitStreamUsing, FromBitStreamWith, LittleEndian, SignedBitCount, ToBitStream,
32    ToBitStreamUsing, write::Overflowed,
33};
34use std::fs::File;
35use std::io::BufReader;
36use std::num::NonZero;
37use std::path::Path;
38
39/// Types related to the CUESHEET metadata block
40pub mod cuesheet;
41
42const FLAC_TAG: &[u8; 4] = b"fLaC";
43
44/// A trait for indicating various pieces of FLAC stream metadata
45///
46/// This metadata may be necessary for decoding a FLAC file
47/// to some other container or an output stream.
48pub trait Metadata {
49    /// Returns channel count
50    ///
51    /// From 1 to 8
52    fn channel_count(&self) -> u8;
53
54    /// Returns channel mask
55    ///
56    /// This uses the channel mask defined
57    /// in the Vorbis comment, if found, or defaults
58    /// to FLAC's default channel assignment if not
59    fn channel_mask(&self) -> ChannelMask {
60        ChannelMask::from_channels(self.channel_count())
61    }
62
63    /// Returns sample rate, in Hz
64    fn sample_rate(&self) -> u32;
65
66    /// Returns decoder's bits-per-sample
67    ///
68    /// From 1 to 32
69    fn bits_per_sample(&self) -> u32;
70
71    /// Returns total number of channel-independent samples, if known
72    fn total_samples(&self) -> Option<u64> {
73        None
74    }
75
76    /// Returns MD5 of entire stream, if known
77    ///
78    /// MD5 is always calculated in terms of little-endian,
79    /// signed, byte-aligned values.
80    fn md5(&self) -> Option<&[u8; 16]> {
81        None
82    }
83
84    /// Returns total length of decoded file, in bytes
85    fn decoded_len(&self) -> Option<u64> {
86        self.total_samples().map(|s| {
87            s * u64::from(self.channel_count()) * u64::from(self.bits_per_sample().div_ceil(8))
88        })
89    }
90
91    /// Returns duration of file
92    fn duration(&self) -> Option<std::time::Duration> {
93        const NANOS_PER_SEC: u64 = 1_000_000_000;
94
95        let sample_rate = u64::from(self.sample_rate());
96
97        self.total_samples().map(|s| {
98            std::time::Duration::new(
99                s / sample_rate,
100                u32::try_from(((s % sample_rate) * NANOS_PER_SEC) / sample_rate)
101                    .unwrap_or_default(),
102            )
103        })
104    }
105}
106
107/// A FLAC metadata block header
108///
109/// | Bits | Field | Meaning |
110/// |-----:|------:|---------|
111/// | 1    | `last` | final metadata block in file |
112/// | 7    | `block_type` | type of block |
113/// | 24   | `size` | block size, in bytes |
114///
115/// # Example
116/// ```
117/// use bitstream_io::{BitReader, BitRead, BigEndian};
118/// use flac_codec::metadata::{BlockHeader, BlockType};
119///
120/// let data: &[u8] = &[0b1_0000000, 0x00, 0x00, 0x22];
121/// let mut r = BitReader::endian(data, BigEndian);
122/// assert_eq!(
123///     r.parse::<BlockHeader>().unwrap(),
124///     BlockHeader {
125///         last: true,                         // 0b1
126///         block_type: BlockType::Streaminfo,  // 0b0000000
127///         size: 0x00_00_22u16.into(),         // 0x00, 0x00, 0x22
128///     },
129/// );
130/// ```
131#[derive(Debug, Eq, PartialEq)]
132pub struct BlockHeader {
133    /// Whether we are the final block
134    pub last: bool,
135    /// Our block type
136    pub block_type: BlockType,
137    /// Our block size, in bytes
138    pub size: BlockSize,
139}
140
141impl BlockHeader {
142    const SIZE: BlockSize = BlockSize((1 + 7 + 24) / 8);
143}
144
145/// A type of FLAC metadata block
146pub trait MetadataBlock:
147    ToBitStream<Error: Into<Error>> + Into<Block> + TryFrom<Block> + Clone
148{
149    /// The metadata block's type
150    const TYPE: BlockType;
151
152    /// Whether the block can occur multiple times in a file
153    const MULTIPLE: bool;
154
155    /// Size of block, in bytes, not including header
156    fn bytes(&self) -> Option<BlockSize> {
157        self.bits::<BlockBits>().ok().map(|b| b.into())
158    }
159
160    /// Size of block, in bytes, including block header
161    fn total_size(&self) -> Option<BlockSize> {
162        self.bytes().and_then(|s| s.checked_add(BlockHeader::SIZE))
163    }
164}
165
166#[derive(Default)]
167struct BlockBits(u32);
168
169impl BlockBits {
170    const MAX: u32 = BlockSize::MAX * 8;
171}
172
173impl From<u8> for BlockBits {
174    fn from(u: u8) -> Self {
175        Self(u.into())
176    }
177}
178
179impl TryFrom<u32> for BlockBits {
180    type Error = (); // the error will be replaced later
181
182    fn try_from(u: u32) -> Result<Self, Self::Error> {
183        (u <= Self::MAX).then_some(Self(u)).ok_or(())
184    }
185}
186
187impl TryFrom<usize> for BlockBits {
188    type Error = (); // the error will be replaced later
189
190    fn try_from(u: usize) -> Result<Self, Self::Error> {
191        u32::try_from(u)
192            .map_err(|_| ())
193            .and_then(|u| (u <= Self::MAX).then_some(Self(u)).ok_or(()))
194    }
195}
196
197impl bitstream_io::write::Counter for BlockBits {
198    fn checked_add_assign(&mut self, Self(b): Self) -> Result<(), Overflowed> {
199        *self = self
200            .0
201            .checked_add(b)
202            .filter(|b| *b <= Self::MAX)
203            .map(Self)
204            .ok_or(Overflowed)?;
205        Ok(())
206    }
207
208    fn checked_mul(self, Self(b): Self) -> Result<Self, Overflowed> {
209        self.0
210            .checked_mul(b)
211            .filter(|b| *b <= Self::MAX)
212            .map(Self)
213            .ok_or(Overflowed)
214    }
215
216    fn byte_aligned(&self) -> bool {
217        self.0.is_multiple_of(8)
218    }
219}
220
221impl From<BlockBits> for BlockSize {
222    fn from(BlockBits(u): BlockBits) -> Self {
223        assert!(u % 8 == 0);
224        Self(u / 8)
225    }
226}
227
228impl BlockHeader {
229    fn new<M: MetadataBlock>(last: bool, block: &M) -> Result<Self, Error> {
230        fn large_block<E: Into<Error>>(err: E) -> Error {
231            match err.into() {
232                Error::Io(_) => Error::ExcessiveBlockSize,
233                e => e,
234            }
235        }
236
237        Ok(Self {
238            last,
239            block_type: M::TYPE,
240            size: block.bits::<BlockBits>().map_err(large_block)?.into(),
241        })
242    }
243}
244
245impl FromBitStream for BlockHeader {
246    type Error = Error;
247
248    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
249        Ok(Self {
250            last: r.read::<1, _>()?,
251            block_type: r.parse()?,
252            size: r.parse()?,
253        })
254    }
255}
256
257impl ToBitStream for BlockHeader {
258    type Error = Error;
259
260    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
261        w.write::<1, _>(self.last)?;
262        w.build(&self.block_type)?;
263        w.build(&self.size)?;
264        Ok(())
265    }
266}
267
268/// A defined FLAC metadata block type
269#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
270pub enum BlockType {
271    /// The STREAMINFO block
272    Streaminfo = 0,
273    /// The PADDING block
274    Padding = 1,
275    /// The APPLICATION block
276    Application = 2,
277    /// The SEEKTABLE block
278    SeekTable = 3,
279    /// The VORBIS_COMMENT block
280    VorbisComment = 4,
281    /// The CUESHEET block
282    Cuesheet = 5,
283    /// The PICTURE block
284    Picture = 6,
285}
286
287impl std::fmt::Display for BlockType {
288    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
289        match self {
290            Self::Streaminfo => "STREAMINFO".fmt(f),
291            Self::Padding => "PADDING".fmt(f),
292            Self::Application => "APPLICATION".fmt(f),
293            Self::SeekTable => "SEEKTABLE".fmt(f),
294            Self::VorbisComment => "VORBIS_COMMENT".fmt(f),
295            Self::Cuesheet => "CUESHEET".fmt(f),
296            Self::Picture => "PICTURE".fmt(f),
297        }
298    }
299}
300
301impl FromBitStream for BlockType {
302    type Error = Error;
303
304    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
305        match r.read::<7, u8>()? {
306            0 => Ok(Self::Streaminfo),
307            1 => Ok(Self::Padding),
308            2 => Ok(Self::Application),
309            3 => Ok(Self::SeekTable),
310            4 => Ok(Self::VorbisComment),
311            5 => Ok(Self::Cuesheet),
312            6 => Ok(Self::Picture),
313            7..=126 => Err(Error::ReservedMetadataBlock),
314            _ => Err(Error::InvalidMetadataBlock),
315        }
316    }
317}
318
319impl ToBitStream for BlockType {
320    type Error = Error;
321
322    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
323        w.write::<7, u8>(match self {
324            Self::Streaminfo => 0,
325            Self::Padding => 1,
326            Self::Application => 2,
327            Self::SeekTable => 3,
328            Self::VorbisComment => 4,
329            Self::Cuesheet => 5,
330            Self::Picture => 6,
331        })
332        .map_err(Error::Io)
333    }
334}
335
336/// A block type for optional FLAC metadata blocks
337///
338/// This is a subset of [`BlockType`] which contains
339/// no STREAMINFO, which is a required block.
340#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
341pub enum OptionalBlockType {
342    /// The PADDING block
343    Padding = 1,
344    /// The APPLICATION block
345    Application = 2,
346    /// The SEEKTABLE block
347    SeekTable = 3,
348    /// The VORBIS_COMMENT block
349    VorbisComment = 4,
350    /// The CUESHEET block
351    Cuesheet = 5,
352    /// The PICTURE block
353    Picture = 6,
354}
355
356impl From<OptionalBlockType> for BlockType {
357    fn from(block: OptionalBlockType) -> Self {
358        match block {
359            OptionalBlockType::Padding => Self::Padding,
360            OptionalBlockType::Application => Self::Application,
361            OptionalBlockType::SeekTable => Self::SeekTable,
362            OptionalBlockType::VorbisComment => Self::VorbisComment,
363            OptionalBlockType::Cuesheet => Self::Cuesheet,
364            OptionalBlockType::Picture => Self::Picture,
365        }
366    }
367}
368
369/// A 24-bit block size value, with safeguards against overflow
370#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
371pub struct BlockSize(u32);
372
373impl BlockSize {
374    /// A value of 0
375    pub const ZERO: BlockSize = BlockSize(0);
376
377    const MAX: u32 = (1 << 24) - 1;
378
379    /// Our current value as a u32
380    fn get(&self) -> u32 {
381        self.0
382    }
383}
384
385impl BlockSize {
386    /// Conditionally add `BlockSize` to ourself
387    pub fn checked_add(self, rhs: Self) -> Option<Self> {
388        self.0
389            .checked_add(rhs.0)
390            .filter(|s| *s <= Self::MAX)
391            .map(Self)
392    }
393
394    /// Conditionally subtract `BlockSize` from ourself
395    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
396        self.0.checked_sub(rhs.0).map(Self)
397    }
398}
399
400impl std::fmt::Display for BlockSize {
401    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
402        self.0.fmt(f)
403    }
404}
405
406impl FromBitStream for BlockSize {
407    type Error = std::io::Error;
408
409    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
410        r.read::<24, _>().map(Self)
411    }
412}
413
414impl ToBitStream for BlockSize {
415    type Error = std::io::Error;
416
417    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
418        w.write::<24, _>(self.0)
419    }
420}
421
422impl From<u8> for BlockSize {
423    fn from(u: u8) -> Self {
424        Self(u.into())
425    }
426}
427
428impl From<u16> for BlockSize {
429    fn from(u: u16) -> Self {
430        Self(u.into())
431    }
432}
433
434impl TryFrom<usize> for BlockSize {
435    type Error = BlockSizeOverflow;
436
437    fn try_from(u: usize) -> Result<Self, Self::Error> {
438        u32::try_from(u)
439            .map_err(|_| BlockSizeOverflow)
440            .and_then(|s| (s <= Self::MAX).then_some(Self(s)).ok_or(BlockSizeOverflow))
441    }
442}
443
444impl TryFrom<u32> for BlockSize {
445    type Error = BlockSizeOverflow;
446
447    fn try_from(u: u32) -> Result<Self, Self::Error> {
448        (u <= Self::MAX).then_some(Self(u)).ok_or(BlockSizeOverflow)
449    }
450}
451
452impl TryFrom<u64> for BlockSize {
453    type Error = BlockSizeOverflow;
454
455    fn try_from(u: u64) -> Result<Self, Self::Error> {
456        u32::try_from(u)
457            .map_err(|_| BlockSizeOverflow)
458            .and_then(|s| (s <= Self::MAX).then_some(Self(s)).ok_or(BlockSizeOverflow))
459    }
460}
461
462impl From<BlockSize> for u32 {
463    #[inline]
464    fn from(size: BlockSize) -> u32 {
465        size.0
466    }
467}
468
469/// An error that occurs when trying to build an overly large `BlockSize`
470#[derive(Copy, Clone, Debug)]
471pub struct BlockSizeOverflow;
472
473impl std::error::Error for BlockSizeOverflow {}
474
475impl std::fmt::Display for BlockSizeOverflow {
476    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
477        "value too large for BlockSize".fmt(f)
478    }
479}
480
481/// An iterator over FLAC metadata blocks
482pub struct BlockIterator<R: std::io::Read> {
483    reader: R,
484    failed: bool,
485    tag_read: bool,
486    streaminfo_read: bool,
487    seektable_read: bool,
488    vorbiscomment_read: bool,
489    png_read: bool,
490    icon_read: bool,
491    finished: bool,
492}
493
494impl<R: std::io::Read> BlockIterator<R> {
495    /// Creates an iterator over something that implements `Read`.
496    /// Because this may perform many small reads,
497    /// performance is greatly improved by buffering reads
498    /// when reading from a raw `File`.
499    pub fn new(reader: R) -> Self {
500        Self {
501            reader,
502            failed: false,
503            tag_read: false,
504            streaminfo_read: false,
505            seektable_read: false,
506            vorbiscomment_read: false,
507            png_read: false,
508            icon_read: false,
509            finished: false,
510        }
511    }
512
513    fn read_block(&mut self) -> Option<Result<Block, Error>> {
514        // like a slighly easier variant of "Take"
515        struct LimitedReader<R> {
516            reader: R,
517            size: usize,
518        }
519
520        impl<R: std::io::Read> std::io::Read for LimitedReader<R> {
521            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
522                let size = self.size.min(buf.len());
523                self.reader.read(&mut buf[0..size]).inspect(|amt_read| {
524                    self.size -= amt_read;
525                })
526            }
527        }
528
529        (!self.finished).then(|| {
530            BitReader::endian(&mut self.reader, BigEndian)
531                .parse()
532                .and_then(|header: BlockHeader| {
533                    let mut reader = BitReader::endian(
534                        LimitedReader {
535                            reader: self.reader.by_ref(),
536                            size: header.size.get().try_into().unwrap(),
537                        },
538                        BigEndian,
539                    );
540
541                    let block = reader.parse_with(&header)?;
542
543                    match reader.into_reader().size {
544                        0 => {
545                            self.finished = header.last;
546                            Ok(block)
547                        }
548                        _ => Err(Error::InvalidMetadataBlockSize),
549                    }
550                })
551        })
552    }
553}
554
555impl<R: std::io::Read> Iterator for BlockIterator<R> {
556    type Item = Result<Block, Error>;
557
558    fn next(&mut self) -> Option<Self::Item> {
559        if self.failed {
560            // once we hit an error, stop any further reads
561            None
562        } else if !self.tag_read {
563            // "fLaC" tag must come before anything else
564            let mut tag = [0; 4];
565            match self.reader.read_exact(&mut tag) {
566                Ok(()) => match &tag {
567                    FLAC_TAG => {
568                        self.tag_read = true;
569                        self.next()
570                    }
571                    _ => {
572                        self.failed = true;
573                        Some(Err(Error::MissingFlacTag))
574                    }
575                },
576                Err(err) => {
577                    self.failed = true;
578                    Some(Err(Error::Io(err)))
579                }
580            }
581        } else if !self.streaminfo_read {
582            // STREAMINFO block must be first in file
583            match self.read_block() {
584                block @ Some(Ok(Block::Streaminfo(_))) => {
585                    self.streaminfo_read = true;
586                    block
587                }
588                _ => {
589                    self.failed = true;
590                    Some(Err(Error::MissingStreaminfo))
591                }
592            }
593        } else {
594            match self.read_block() {
595                Some(Ok(Block::Streaminfo(_))) => Some(Err(Error::MultipleStreaminfo)),
596                seektable @ Some(Ok(Block::SeekTable(_))) => {
597                    if !self.seektable_read {
598                        self.seektable_read = true;
599                        seektable
600                    } else {
601                        self.failed = true;
602                        Some(Err(Error::MultipleSeekTable))
603                    }
604                }
605                vorbiscomment @ Some(Ok(Block::VorbisComment(_))) => {
606                    if !self.vorbiscomment_read {
607                        self.vorbiscomment_read = true;
608                        vorbiscomment
609                    } else {
610                        self.failed = true;
611                        Some(Err(Error::MultipleVorbisComment))
612                    }
613                }
614                picture @ Some(Ok(Block::Picture(Picture {
615                    picture_type: PictureType::Png32x32,
616                    ..
617                }))) => {
618                    if !self.png_read {
619                        self.png_read = true;
620                        picture
621                    } else {
622                        self.failed = true;
623                        Some(Err(Error::MultiplePngIcon))
624                    }
625                }
626                picture @ Some(Ok(Block::Picture(Picture {
627                    picture_type: PictureType::GeneralFileIcon,
628                    ..
629                }))) => {
630                    if !self.icon_read {
631                        self.icon_read = true;
632                        picture
633                    } else {
634                        self.failed = true;
635                        Some(Err(Error::MultipleGeneralIcon))
636                    }
637                }
638                block @ Some(Err(_)) => {
639                    self.failed = true;
640                    block
641                }
642                block => block,
643            }
644        }
645    }
646}
647
648/// Returns iterator of blocks from the given reader
649///
650/// The reader should be positioned at the start of the FLAC
651/// file.
652///
653/// Because this may perform many small reads,
654/// using a buffered reader may greatly improve performance
655/// when reading from a raw `File`.
656///
657/// # Example
658///
659/// ```
660/// use flac_codec::{
661///     metadata::{read_blocks, Application, Block},
662///     encode::{FlacSampleWriter, Options},
663/// };
664/// use std::io::{Cursor, Seek};
665///
666/// let mut flac = Cursor::new(vec![]);  // a FLAC file in memory
667///
668/// // add some APPLICATION blocks at encode-time
669/// let application_1 = Application {id: 0x1234, data: vec![1, 2, 3, 4]};
670/// let application_2 = Application {id: 0x5678, data: vec![5, 6, 7, 8]};
671///
672/// let options = Options::default()
673///     .application(application_1.clone())
674///     .application(application_2.clone())
675///     .no_padding()
676///     .no_seektable();
677///
678/// let mut writer = FlacSampleWriter::new(
679///     &mut flac,  // our wrapped writer
680///     options,    // our encoding options
681///     44100,      // sample rate
682///     16,         // bits-per-sample
683///     1,          // channel count
684///     Some(1),    // total samples
685/// ).unwrap();
686///
687/// // write a simple FLAC file
688/// writer.write(std::slice::from_ref(&0)).unwrap();
689/// writer.finalize().unwrap();
690///
691/// flac.rewind().unwrap();
692///
693/// // read blocks from encoded file
694/// let blocks = read_blocks(flac)
695///     .skip(1)  // skip STREAMINFO block
696///     .collect::<Result<Vec<Block>, _>>()
697///     .unwrap();
698///
699/// // ensure they match our APPLICATION blocks
700/// assert_eq!(blocks, vec![application_1.into(), application_2.into()]);
701/// ```
702pub fn read_blocks<R: std::io::Read>(r: R) -> BlockIterator<R> {
703    BlockIterator::new(r)
704}
705
706/// Returns iterator of blocks from the given path
707///
708/// # Errors
709///
710/// Returns any I/O error from opening the path.
711/// Note that the iterator itself may return any errors
712/// from reading individual blocks.
713pub fn blocks<P: AsRef<Path>>(p: P) -> std::io::Result<BlockIterator<BufReader<File>>> {
714    File::open(p.as_ref()).map(|f| read_blocks(BufReader::new(f)))
715}
716
717/// Returns first instance of the given block from the given reader
718///
719/// The reader should be positioned at the start of the FLAC
720/// file.
721///
722/// Because this may perform many small reads,
723/// using a buffered reader may greatly improve performance
724/// when reading from a raw `File`.
725///
726/// # Example
727///
728/// ```
729/// use flac_codec::{
730///     metadata::{read_block, Streaminfo},
731///     encode::{FlacSampleWriter, Options},
732/// };
733/// use std::io::{Cursor, Seek};
734///
735/// let mut flac: Cursor<Vec<u8>> = Cursor::new(vec![]);  // a FLAC file in memory
736///
737/// let mut writer = FlacSampleWriter::new(
738///     &mut flac,           // our wrapped writer
739///     Options::default(),  // default encoding options
740///     44100,               // sample rate
741///     16,                  // bits-per-sample
742///     1,                   // channel count
743///     Some(1),             // total samples
744/// ).unwrap();
745///
746/// // write a simple FLAC file
747/// writer.write(std::slice::from_ref(&0)).unwrap();
748/// writer.finalize().unwrap();
749///
750/// flac.rewind().unwrap();
751///
752/// // STREAMINFO block must always be present
753/// let streaminfo = match read_block::<_, Streaminfo>(flac) {
754///     Ok(Some(streaminfo)) => streaminfo,
755///     _ => panic!("STREAMINFO not found"),
756/// };
757///
758/// // verify STREAMINFO fields against encoding parameters
759/// assert_eq!(streaminfo.sample_rate, 44100);
760/// assert_eq!(u32::from(streaminfo.bits_per_sample), 16);
761/// assert_eq!(streaminfo.channels.get(), 1);
762/// assert_eq!(streaminfo.total_samples.map(|s| s.get()), Some(1));
763/// ```
764pub fn read_block<R, B>(r: R) -> Result<Option<B>, Error>
765where
766    R: std::io::Read,
767    B: MetadataBlock,
768{
769    read_blocks(r)
770        .find_map(|r| r.map(|b| B::try_from(b).ok()).transpose())
771        .transpose()
772}
773
774/// Returns first instance of the given block from the given path
775///
776/// See the [`read_block`] for additional information.
777///
778/// # Errors
779///
780/// Returns any error from opening the path, or from reading
781/// blocks from the path.
782pub fn block<P, B>(p: P) -> Result<Option<B>, Error>
783where
784    P: AsRef<Path>,
785    B: MetadataBlock,
786{
787    blocks(p).map_err(Error::Io).and_then(|mut blocks| {
788        blocks
789            .find_map(|r| r.map(|b| B::try_from(b).ok()).transpose())
790            .transpose()
791    })
792}
793
794/// Returns FLAC's STREAMINFO metadata block from the given file
795///
796/// # Errors
797///
798/// Returns an error if the STREAMINFO block is not first
799/// or if any I/O error occurs when reading the file.
800pub fn info<P: AsRef<Path>>(p: P) -> Result<Streaminfo, Error> {
801    File::open(p)
802        .map_err(Error::Io)
803        .and_then(|f| read_info(BufReader::new(f)))
804}
805
806/// Returns FLAC's STREAMINFO metadata block from the given reader
807///
808/// The reader is assumed to be rewound to the start of the FLAC file data.
809///
810/// # Errors
811///
812/// Returns an error if the STREAMINFO block is not first
813/// or if any I/O error occurs when reading the file.
814pub fn read_info<R: std::io::Read>(r: R) -> Result<Streaminfo, Error> {
815    let mut r = BitReader::endian(r, BigEndian);
816
817    // FLAC tag must be first thing in stream
818    if &r.read_to::<[u8; 4]>()? != FLAC_TAG {
819        return Err(Error::MissingFlacTag);
820    }
821
822    // STREAMINFO block must be present, and must be first
823    if !matches!(
824        r.parse()?,
825        BlockHeader {
826            block_type: BlockType::Streaminfo,
827            size: Streaminfo::SIZE,
828            last: _,
829        }
830    ) {
831        return Err(Error::MissingStreaminfo);
832    }
833
834    // Finally, parse STREAMINFO block itself
835    r.parse().map_err(Error::Io)
836}
837
838/// Returns iterator of blocks of a given type
839pub fn blocks_of<P, B>(p: P) -> impl Iterator<Item = Result<B, Error>>
840where
841    P: AsRef<Path>,
842    B: MetadataBlock + 'static,
843{
844    match blocks(p) {
845        Ok(iter) => {
846            Box::new(iter.filter_map(|block| block.map(|b| B::try_from(b).ok()).transpose()))
847                as Box<dyn Iterator<Item = Result<B, Error>>>
848        }
849        Err(e) => Box::new(std::iter::once(Err(e.into()))),
850    }
851}
852
853/// Writes iterator of blocks to the given writer.
854///
855/// Because this may perform many small writes,
856/// buffering writes may greatly improve performance
857/// when writing to a raw `File`.
858///
859/// let picture_type = picture.picture_type;
860/// # Errorsprintln!("  type: {} ({})", picture_type as u8, picture_type);
861///
862/// Passes along any I/O errors from the underlying stream.
863/// May also generate an error if any of the blocks are invalid
864/// (e.g. STREAMINFO not being the first block, any block is too large, etc.).
865///
866/// # Example
867///
868/// ```
869/// use flac_codec::metadata::{
870///     write_blocks, read_blocks, Streaminfo, Application, Block,
871/// };
872/// use std::io::{Cursor, Seek};
873///
874/// let mut flac: Cursor<Vec<u8>> = Cursor::new(vec![]);  // a FLAC file in memory
875///
876/// // our test blocks
877/// let blocks: Vec<Block> = vec![
878///     Streaminfo {
879///         minimum_block_size: 0,
880///         maximum_block_size: 0,
881///         minimum_frame_size: None,
882///         maximum_frame_size: None,
883///         sample_rate: 44100,
884///         channels: 1u8.try_into().unwrap(),
885///         bits_per_sample: 16u32.try_into().unwrap(),
886///         total_samples: None,
887///         md5: None,
888///     }.into(),
889///     Application {id: 0x1234, data: vec![1, 2, 3, 4]}.into(),
890///     Application {id: 0x5678, data: vec![5, 6, 7, 8]}.into(),
891/// ];
892///
893/// // write our test blocks to a file
894/// write_blocks(&mut flac, blocks.clone()).unwrap();
895///
896/// flac.rewind().unwrap();
897///
898/// // read our blocks back from that file
899/// let read_blocks = read_blocks(flac).collect::<Result<Vec<Block>, _>>().unwrap();
900///
901/// // they should be identical
902/// assert_eq!(blocks, read_blocks);
903/// ```
904pub fn write_blocks<B: AsBlockRef>(
905    mut w: impl std::io::Write,
906    blocks: impl IntoIterator<Item = B>,
907) -> Result<(), Error> {
908    fn iter_last<T>(i: impl Iterator<Item = T>) -> impl Iterator<Item = (bool, T)> {
909        let mut iter = i.peekable();
910
911        std::iter::from_fn(move || {
912            let item = iter.next()?;
913            Some((iter.peek().is_none(), item))
914        })
915    }
916
917    // "FlaC" tag must come before anything else
918    w.write_all(FLAC_TAG).map_err(Error::Io)?;
919
920    let mut w = bitstream_io::BitWriter::endian(w, BigEndian);
921    let mut blocks = iter_last(blocks.into_iter());
922
923    // STREAMINFO block must be present and must be first in file
924    let next = blocks.next();
925    match next.as_ref().map(|(last, b)| (last, b.as_block_ref())) {
926        Some((last, streaminfo @ BlockRef::Streaminfo(_))) => w.build_using(&streaminfo, *last)?,
927        _ => return Err(Error::MissingStreaminfo),
928    }
929
930    // certain other blocks in the file must only occur once at most
931    let mut seektable_read = false;
932    let mut vorbiscomment_read = false;
933    let mut png_read = false;
934    let mut icon_read = false;
935
936    blocks.try_for_each(|(last, block)| match block.as_block_ref() {
937        BlockRef::Streaminfo(_) => Err(Error::MultipleStreaminfo),
938        vorbiscomment @ BlockRef::VorbisComment(_) => match vorbiscomment_read {
939            false => {
940                vorbiscomment_read = true;
941                w.build_using(&vorbiscomment.as_block_ref(), last)
942            }
943            true => Err(Error::MultipleVorbisComment),
944        },
945        seektable @ BlockRef::SeekTable(_) => match seektable_read {
946            false => {
947                seektable_read = true;
948                w.build_using(&seektable.as_block_ref(), last)
949            }
950            true => Err(Error::MultipleSeekTable),
951        },
952        picture @ BlockRef::Picture(Picture {
953            picture_type: PictureType::Png32x32,
954            ..
955        }) => {
956            if !png_read {
957                png_read = true;
958                w.build_using(&picture.as_block_ref(), last)
959            } else {
960                Err(Error::MultiplePngIcon)
961            }
962        }
963        picture @ BlockRef::Picture(Picture {
964            picture_type: PictureType::GeneralFileIcon,
965            ..
966        }) => {
967            if !icon_read {
968                icon_read = true;
969                w.build_using(&picture.as_block_ref(), last)
970            } else {
971                Err(Error::MultipleGeneralIcon)
972            }
973        }
974        block => w.build_using(&block.as_block_ref(), last),
975    })
976}
977
978/// Given a Path, attempts to update FLAC metadata blocks
979///
980/// Returns `true` if the file was completely rebuilt,
981/// or `false` if the original was overwritten.
982///
983/// # Errors
984///
985/// Returns error if unable to read metadata blocks,
986/// unable to write blocks, or if the existing or updated
987/// blocks do not conform to the FLAC file specification.
988pub fn update<P, E>(path: P, f: impl FnOnce(&mut BlockList) -> Result<(), E>) -> Result<bool, E>
989where
990    P: AsRef<Path>,
991    E: From<Error>,
992{
993    use std::fs::OpenOptions;
994
995    update_file(
996        OpenOptions::new()
997            .read(true)
998            .write(true)
999            .truncate(false)
1000            .create(false)
1001            .open(path.as_ref())
1002            .map_err(Error::Io)?,
1003        || std::fs::File::create(path.as_ref()),
1004        f,
1005    )
1006}
1007
1008/// Given open file, attempts to update its metadata blocks
1009///
1010/// The original file should be rewound to the start of the stream.
1011///
1012/// Applies closure `f` to the blocks and attempts to update them.
1013///
1014/// If the updated blocks can be made the same size as the
1015/// original file by adjusting padding, the file will be
1016/// partially overwritten with new contents.
1017///
1018/// If the new blocks are too large (or small) to fit into
1019/// the original file, the original unmodified file is dropped
1020/// and the `rebuilt` closure is called to build a new
1021/// file.  The file's contents are then dumped into the new file.
1022///
1023/// Returns `true` if the file was completely rebuilt,
1024/// or `false` if the original was overwritten.
1025///
1026/// # Example 1
1027///
1028/// ```
1029/// use flac_codec::{
1030///     metadata::{update_file, BlockList, Padding, VorbisComment},
1031///     metadata::fields::TITLE,
1032///     encode::{FlacSampleWriter, Options},
1033/// };
1034/// use std::io::{Cursor, Seek};
1035///
1036/// let mut flac = Cursor::new(vec![]);  // a FLAC file in memory
1037///
1038/// // include a small amount of padding
1039/// const PADDING_SIZE: u32 = 100;
1040/// let options = Options::default().padding(PADDING_SIZE).unwrap();
1041///
1042/// let mut writer = FlacSampleWriter::new(
1043///     &mut flac,  // our wrapped writer
1044///     options,    // our encoding options
1045///     44100,      // sample rate
1046///     16,         // bits-per-sample
1047///     1,          // channel count
1048///     Some(1),    // total samples
1049/// ).unwrap();
1050///
1051/// // write a simple FLAC file
1052/// writer.write(std::slice::from_ref(&0)).unwrap();
1053/// writer.finalize().unwrap();
1054///
1055/// flac.rewind().unwrap();
1056///
1057/// let mut rebuilt: Vec<u8> = vec![];
1058///
1059/// // update file with new Vorbis Comment
1060/// assert!(matches!(
1061///     update_file::<_, _, flac_codec::Error>(
1062///         // our original file
1063///         &mut flac,
1064///         // a closure to create a new file, if necessary
1065///         || Ok(&mut rebuilt),
1066///         // the closure that performs the metadata update
1067///         |blocklist| {
1068///             blocklist.update::<VorbisComment>(
1069///                 // the blocklist itself has a closure
1070///                 // that updates a block, creating it if necessary
1071///                 // (in this case, we're updating the Vorbis comment)
1072///                 |vc| vc.set(TITLE, "Track Title")
1073///             );
1074///             Ok(())
1075///         },
1076///     ),
1077///     Ok(false),  // false indicates the original file was updated
1078/// ));
1079///
1080/// flac.rewind().unwrap();
1081///
1082/// // re-read the metadata blocks from the original file
1083/// let blocks = BlockList::read(flac).unwrap();
1084///
1085/// // the original file now has a Vorbis Comment block
1086/// // with the track title that we added
1087/// assert_eq!(
1088///     blocks.get::<VorbisComment>().and_then(|vc| vc.get(TITLE)),
1089///     Some("Track Title"),
1090/// );
1091///
1092/// // the original file's padding block is smaller than before
1093/// // to accomodate our new Vorbis Comment block
1094/// assert!(u32::from(blocks.get::<Padding>().unwrap().size) < PADDING_SIZE);
1095///
1096/// // and the unneeded rebuilt file remains empty
1097/// assert!(rebuilt.is_empty());
1098/// ```
1099///
1100/// # Example 2
1101///
1102/// ```
1103/// use flac_codec::{
1104///     metadata::{update_file, BlockList, VorbisComment},
1105///     metadata::fields::TITLE,
1106///     encode::{FlacSampleWriter, Options},
1107/// };
1108/// use std::io::{Cursor, Seek};
1109///
1110/// let mut flac = Cursor::new(vec![]);  // a FLAC file in memory
1111///
1112/// // include no padding in our encoded file
1113/// let options = Options::default().no_padding();
1114///
1115/// let mut writer = FlacSampleWriter::new(
1116///     &mut flac,  // our wrapped writer
1117///     options,    // our encoding options
1118///     44100,      // sample rate
1119///     16,         // bits-per-sample
1120///     1,          // channel count
1121///     Some(1),    // total samples
1122/// ).unwrap();
1123///
1124/// // write a simple FLAC file
1125/// writer.write(std::slice::from_ref(&0)).unwrap();
1126/// writer.finalize().unwrap();
1127///
1128/// flac.rewind().unwrap();
1129///
1130/// let mut rebuilt: Vec<u8> = vec![];
1131///
1132/// // update file with new Vorbis Comment
1133/// assert!(matches!(
1134///     update_file::<_, _, flac_codec::Error>(
1135///         // our original file
1136///         &mut flac,
1137///         // a closure to create a new file, if necessary
1138///         || Ok(&mut rebuilt),
1139///         // the closure that performs the metadata update
1140///         |blocklist| {
1141///             blocklist.update::<VorbisComment>(
1142///                 // the blocklist itself has a closure
1143///                 // that updates a block, creating it if necessary
1144///                 // (in this case, we're updating the Vorbis comment)
1145///                 |vc| vc.set(TITLE, "Track Title")
1146///             );
1147///             Ok(())
1148///         },
1149///     ),
1150///     Ok(true),  // true indicates the original file was not updated
1151/// ));
1152///
1153/// flac.rewind().unwrap();
1154///
1155/// // re-read the metadata blocks from the original file
1156/// let blocks = BlockList::read(flac).unwrap();
1157///
1158/// // the original file remains unchanged
1159/// // and has no Vorbis Comment block
1160/// assert_eq!(blocks.get::<VorbisComment>(), None);
1161///
1162/// // now read the metadata blocks from the rebuilt file
1163/// let blocks = BlockList::read(rebuilt.as_slice()).unwrap();
1164///
1165/// // the rebuilt file has our Vorbis Comment entry instead
1166/// assert_eq!(
1167///     blocks.get::<VorbisComment>().and_then(|vc| vc.get(TITLE)),
1168///     Some("Track Title"),
1169/// );
1170/// ```
1171pub fn update_file<F, N, E>(
1172    mut original: F,
1173    rebuilt: impl FnOnce() -> std::io::Result<N>,
1174    f: impl FnOnce(&mut BlockList) -> Result<(), E>,
1175) -> Result<bool, E>
1176where
1177    F: std::io::Read + std::io::Seek + std::io::Write,
1178    N: std::io::Write,
1179    E: From<Error>,
1180{
1181    use crate::Counter;
1182    use std::cmp::Ordering;
1183    use std::io::{BufReader, BufWriter, Read, sink};
1184
1185    fn rebuild_file<N, R>(
1186        rebuilt: impl FnOnce() -> std::io::Result<N>,
1187        mut r: R,
1188        blocks: BlockList,
1189    ) -> Result<(), Error>
1190    where
1191        N: std::io::Write,
1192        R: Read,
1193    {
1194        // dump our new blocks and remaining FLAC data to temp file
1195        let mut tmp = Vec::new();
1196        write_blocks(&mut tmp, blocks)?;
1197        std::io::copy(&mut r, &mut tmp).map_err(Error::Io)?;
1198        drop(r);
1199
1200        // fresh original file and rewrite it with tmp file contents
1201        rebuilt()
1202            .and_then(|mut f| f.write_all(tmp.as_slice()))
1203            .map_err(Error::Io)
1204    }
1205
1206    /// Returns Ok if successful
1207    fn grow_padding(blocks: &mut BlockList, more_bytes: u64) -> Result<(), ()> {
1208        // if a block set has more than one PADDING, we'll try the first
1209        // rather than attempt to grow each in turn
1210        //
1211        // this is the most common case
1212        let padding = blocks.get_mut::<Padding>().ok_or(())?;
1213
1214        padding.size = padding
1215            .size
1216            .checked_add(more_bytes.try_into().map_err(|_| ())?)
1217            .ok_or(())?;
1218
1219        Ok(())
1220    }
1221
1222    /// Returns Ok if successful
1223    fn shrink_padding(blocks: &mut BlockList, fewer_bytes: u64) -> Result<(), ()> {
1224        // if a block set has more than one PADDING, we'll try the first
1225        // rather than attempt to shrink each in turn
1226        //
1227        // this is the most common case
1228        let padding = blocks.get_mut::<Padding>().ok_or(())?;
1229
1230        padding.size = padding
1231            .size
1232            .checked_sub(fewer_bytes.try_into().map_err(|_| ())?)
1233            .ok_or(())?;
1234
1235        Ok(())
1236    }
1237
1238    // the starting position in the stream we rewind to
1239    let start = std::io::SeekFrom::Start(original.stream_position().map_err(Error::Io)?);
1240
1241    let mut reader = Counter::new(BufReader::new(&mut original));
1242
1243    let mut blocks = BlockList::read(Read::by_ref(&mut reader))?;
1244
1245    let Counter {
1246        stream: reader,
1247        count: old_size,
1248    } = reader;
1249
1250    f(&mut blocks)?;
1251
1252    let new_size = {
1253        let mut new_size = Counter::new(sink());
1254        write_blocks(&mut new_size, blocks.blocks())?;
1255        new_size.count
1256    };
1257
1258    match new_size.cmp(&old_size) {
1259        Ordering::Less => {
1260            // blocks have shrunk in size, so try to expand
1261            // PADDING block to hold additional bytes
1262            match grow_padding(&mut blocks, old_size - new_size) {
1263                Ok(()) => {
1264                    original.seek(start).map_err(Error::Io)?;
1265                    write_blocks(BufWriter::new(original), blocks)
1266                        .map(|()| false)
1267                        .map_err(E::from)
1268                }
1269                Err(()) => rebuild_file(rebuilt, reader, blocks)
1270                    .map(|()| true)
1271                    .map_err(E::from),
1272            }
1273        }
1274        Ordering::Equal => {
1275            // blocks are the same size, so no need to adjust padding
1276            original.seek(start).map_err(Error::Io)?;
1277            write_blocks(BufWriter::new(original), blocks)
1278                .map(|()| false)
1279                .map_err(E::from)
1280        }
1281        Ordering::Greater => {
1282            // blocks have grown in size, so try to shrink
1283            // PADDING block to hold additional bytes
1284            match shrink_padding(&mut blocks, new_size - old_size) {
1285                Ok(()) => {
1286                    original.seek(start).map_err(Error::Io)?;
1287                    write_blocks(BufWriter::new(original), blocks)
1288                        .map(|()| false)
1289                        .map_err(E::from)
1290                }
1291                Err(()) => rebuild_file(rebuilt, reader, blocks)
1292                    .map(|()| true)
1293                    .map_err(E::from),
1294            }
1295        }
1296    }
1297}
1298
1299/// Any possible FLAC metadata block
1300///
1301/// Each block consists of a [`BlockHeader`] followed by the block's contents.
1302///
1303/// ```text
1304/// ┌──────────┬────────┬┄┄┄┄┄┄┄┄┬┄┄┄┬────────┬┄┄┄┄┄┄┄┄┬┄┄┄╮
1305/// │ FLAC Tag │ Block₀ │ Block₁ ┆ … ┆ Frame₀ │ Frame₁ ┆ … ┆ FLAC File
1306/// └──────────┼────────┼┄┄┄┄┄┄┄┄┴┄┄┄┴────────┴┄┄┄┄┄┄┄┄┴┄┄┄╯
1307/// ╭──────────╯        ╰────────────────────────╮
1308/// ├──────────────┬─────────────────────────────┤
1309/// │ Block Header │     Metadata Block Data     │           Metadata Block
1310/// └──────────────┴─────────────────────────────┘
1311/// ```
1312#[derive(Debug, Clone, Eq, PartialEq)]
1313pub enum Block {
1314    /// The STREAMINFO block
1315    Streaminfo(Streaminfo),
1316    /// The PADDING block
1317    Padding(Padding),
1318    /// The APPLICATION block
1319    Application(Application),
1320    /// The SEEKTABLE block
1321    SeekTable(SeekTable),
1322    /// The VORBIS_COMMENT block
1323    VorbisComment(VorbisComment),
1324    /// The CUESHEET block
1325    Cuesheet(Cuesheet),
1326    /// The PICTURE block
1327    Picture(Picture),
1328}
1329
1330impl Block {
1331    /// Our block type
1332    pub fn block_type(&self) -> BlockType {
1333        match self {
1334            Self::Streaminfo(_) => BlockType::Streaminfo,
1335            Self::Padding(_) => BlockType::Padding,
1336            Self::Application(_) => BlockType::Application,
1337            Self::SeekTable(_) => BlockType::SeekTable,
1338            Self::VorbisComment(_) => BlockType::VorbisComment,
1339            Self::Cuesheet(_) => BlockType::Cuesheet,
1340            Self::Picture(_) => BlockType::Picture,
1341        }
1342    }
1343}
1344
1345impl AsBlockRef for Block {
1346    fn as_block_ref(&self) -> BlockRef<'_> {
1347        match self {
1348            Self::Streaminfo(s) => BlockRef::Streaminfo(s),
1349            Self::Padding(p) => BlockRef::Padding(p),
1350            Self::Application(a) => BlockRef::Application(a),
1351            Self::SeekTable(s) => BlockRef::SeekTable(s),
1352            Self::VorbisComment(v) => BlockRef::VorbisComment(v),
1353            Self::Cuesheet(v) => BlockRef::Cuesheet(v),
1354            Self::Picture(p) => BlockRef::Picture(p),
1355        }
1356    }
1357}
1358
1359impl FromBitStreamWith<'_> for Block {
1360    type Context = BlockHeader;
1361    type Error = Error;
1362
1363    // parses from reader without header
1364    fn from_reader<R: BitRead + ?Sized>(
1365        r: &mut R,
1366        header: &BlockHeader,
1367    ) -> Result<Self, Self::Error> {
1368        match header.block_type {
1369            BlockType::Streaminfo => Ok(Block::Streaminfo(r.parse()?)),
1370            BlockType::Padding => Ok(Block::Padding(r.parse_using(header.size)?)),
1371            BlockType::Application => Ok(Block::Application(r.parse_using(header.size)?)),
1372            BlockType::SeekTable => Ok(Block::SeekTable(r.parse_using(header.size)?)),
1373            BlockType::VorbisComment => Ok(Block::VorbisComment(r.parse()?)),
1374            BlockType::Cuesheet => Ok(Block::Cuesheet(r.parse()?)),
1375            BlockType::Picture => Ok(Block::Picture(r.parse()?)),
1376        }
1377    }
1378}
1379
1380impl ToBitStreamUsing for Block {
1381    type Context = bool;
1382    type Error = Error;
1383
1384    // builds to writer with header
1385    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W, is_last: bool) -> Result<(), Error> {
1386        match self {
1387            Self::Streaminfo(streaminfo) => w
1388                .build(&BlockHeader::new(is_last, streaminfo)?)
1389                .and_then(|()| w.build(streaminfo).map_err(Error::Io)),
1390            Self::Padding(padding) => w
1391                .build(&BlockHeader::new(is_last, padding)?)
1392                .and_then(|()| w.build(padding).map_err(Error::Io)),
1393            Self::Application(application) => w
1394                .build(&BlockHeader::new(is_last, application)?)
1395                .and_then(|()| w.build(application).map_err(Error::Io)),
1396            Self::SeekTable(seektable) => w
1397                .build(&BlockHeader::new(is_last, seektable)?)
1398                .and_then(|()| w.build(seektable)),
1399            Self::VorbisComment(vorbis_comment) => w
1400                .build(&BlockHeader::new(is_last, vorbis_comment)?)
1401                .and_then(|()| w.build(vorbis_comment)),
1402            Self::Cuesheet(cuesheet) => w
1403                .build(&BlockHeader::new(is_last, cuesheet)?)
1404                .and_then(|()| w.build(cuesheet)),
1405            Self::Picture(picture) => w
1406                .build(&BlockHeader::new(is_last, picture)?)
1407                .and_then(|()| w.build(picture)),
1408        }
1409    }
1410}
1411
1412/// A shared reference to a metadata block
1413#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1414pub enum BlockRef<'b> {
1415    /// The STREAMINFO block
1416    Streaminfo(&'b Streaminfo),
1417    /// The PADDING block
1418    Padding(&'b Padding),
1419    /// The APPLICATION block
1420    Application(&'b Application),
1421    /// The SEEKTABLE block
1422    SeekTable(&'b SeekTable),
1423    /// The VORBIS_COMMENT block
1424    VorbisComment(&'b VorbisComment),
1425    /// The CUESHEET block
1426    Cuesheet(&'b Cuesheet),
1427    /// The PICTURE block
1428    Picture(&'b Picture),
1429}
1430
1431impl BlockRef<'_> {
1432    /// Our block type
1433    pub fn block_type(&self) -> BlockType {
1434        match self {
1435            Self::Streaminfo(_) => BlockType::Streaminfo,
1436            Self::Padding(_) => BlockType::Padding,
1437            Self::Application(_) => BlockType::Application,
1438            Self::SeekTable(_) => BlockType::SeekTable,
1439            Self::VorbisComment(_) => BlockType::VorbisComment,
1440            Self::Cuesheet(_) => BlockType::Cuesheet,
1441            Self::Picture(_) => BlockType::Picture,
1442        }
1443    }
1444}
1445
1446impl AsBlockRef for BlockRef<'_> {
1447    fn as_block_ref(&self) -> BlockRef<'_> {
1448        *self
1449    }
1450}
1451
1452/// A trait for items which can make cheap [`BlockRef`] values.
1453pub trait AsBlockRef {
1454    /// Returns fresh reference to ourself.
1455    fn as_block_ref(&self) -> BlockRef<'_>;
1456}
1457
1458impl<T: AsBlockRef> AsBlockRef for &T {
1459    fn as_block_ref(&self) -> BlockRef<'_> {
1460        <T as AsBlockRef>::as_block_ref(*self)
1461    }
1462}
1463
1464impl ToBitStreamUsing for BlockRef<'_> {
1465    type Context = bool;
1466    type Error = Error;
1467
1468    // builds to writer with header
1469    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W, is_last: bool) -> Result<(), Error> {
1470        match self {
1471            Self::Streaminfo(streaminfo) => w
1472                .build(&BlockHeader::new(is_last, *streaminfo)?)
1473                .and_then(|()| w.build(*streaminfo).map_err(Error::Io)),
1474            Self::Padding(padding) => w
1475                .build(&BlockHeader::new(is_last, *padding)?)
1476                .and_then(|()| w.build(*padding).map_err(Error::Io)),
1477            Self::Application(application) => w
1478                .build(&BlockHeader::new(is_last, *application)?)
1479                .and_then(|()| w.build(*application).map_err(Error::Io)),
1480            Self::SeekTable(seektable) => w
1481                .build(&BlockHeader::new(is_last, *seektable)?)
1482                .and_then(|()| w.build(*seektable)),
1483            Self::VorbisComment(vorbis_comment) => w
1484                .build(&BlockHeader::new(is_last, *vorbis_comment)?)
1485                .and_then(|()| w.build(*vorbis_comment)),
1486            Self::Cuesheet(cuesheet) => w
1487                .build(&BlockHeader::new(is_last, *cuesheet)?)
1488                .and_then(|()| w.build(*cuesheet)),
1489            Self::Picture(picture) => w
1490                .build(&BlockHeader::new(is_last, *picture)?)
1491                .and_then(|()| w.build(*picture)),
1492        }
1493    }
1494}
1495
1496macro_rules! block {
1497    ($t:ty, $v:ident, $m:literal) => {
1498        impl MetadataBlock for $t {
1499            const TYPE: BlockType = BlockType::$v;
1500            const MULTIPLE: bool = $m;
1501        }
1502
1503        impl From<$t> for Block {
1504            fn from(b: $t) -> Self {
1505                Self::$v(b)
1506            }
1507        }
1508
1509        impl TryFrom<Block> for $t {
1510            type Error = ();
1511
1512            fn try_from(block: Block) -> Result<Self, ()> {
1513                match block {
1514                    Block::$v(block) => Ok(block),
1515                    _ => Err(()),
1516                }
1517            }
1518        }
1519
1520        impl AsBlockRef for $t {
1521            fn as_block_ref(&self) -> BlockRef<'_> {
1522                BlockRef::$v(self)
1523            }
1524        }
1525    };
1526}
1527
1528macro_rules! optional_block {
1529    ($t:ty, $v:ident) => {
1530        impl OptionalMetadataBlock for $t {
1531            const OPTIONAL_TYPE: OptionalBlockType = OptionalBlockType::$v;
1532        }
1533
1534        impl private::OptionalMetadataBlock for $t {
1535            fn try_from_opt_block(
1536                block: &private::OptionalBlock,
1537            ) -> Result<&Self, &private::OptionalBlock> {
1538                match block {
1539                    private::OptionalBlock::$v(block) => Ok(block),
1540                    block => Err(block),
1541                }
1542            }
1543
1544            fn try_from_opt_block_mut(
1545                block: &mut private::OptionalBlock,
1546            ) -> Result<&mut Self, &mut private::OptionalBlock> {
1547                match block {
1548                    private::OptionalBlock::$v(block) => Ok(block),
1549                    block => Err(block),
1550                }
1551            }
1552        }
1553
1554        impl From<$t> for private::OptionalBlock {
1555            fn from(vorbis: $t) -> Self {
1556                private::OptionalBlock::$v(vorbis)
1557            }
1558        }
1559
1560        impl TryFrom<private::OptionalBlock> for $t {
1561            type Error = ();
1562
1563            fn try_from(block: private::OptionalBlock) -> Result<Self, ()> {
1564                match block {
1565                    private::OptionalBlock::$v(b) => Ok(b),
1566                    _ => Err(()),
1567                }
1568            }
1569        }
1570    };
1571}
1572
1573/// A STREAMINFO metadata block
1574///
1575/// This block contains metadata about the stream's contents.
1576///
1577/// It must *always* be present in a FLAC file,
1578/// must *always* be the first metadata block in the stream,
1579/// and must *not* be present more than once.
1580///
1581/// | Bits | Field | Meaning |
1582/// |-----:|------:|---------|
1583/// | 16   | `minimum_block_size` | minimum block size (in samples) in the stream
1584/// | 16   | `maximum_block_size` | maximum block size (in samples) in the stream
1585/// | 24   | `minimum_frame_size` | minimum frame size (in bytes) in the stream
1586/// | 24   | `maximum_frame_size` | maximum frame size (in bytes) in the stream
1587/// | 20   | `sample_rate` | stream's sample rate, in Hz
1588/// | 3    | `channels` | stream's channel count (+1)
1589/// | 5    | `bits_per_sample` | stream's bits-per-sample (+1)
1590/// | 36   | `total_samples` | stream's total channel-independent samples
1591/// | 16×8 | `md5` | decoded stream's MD5 sum hash
1592///
1593/// # Example
1594/// ```
1595/// use bitstream_io::{BitReader, BitRead, BigEndian, SignedBitCount};
1596/// use flac_codec::metadata::Streaminfo;
1597/// use std::num::NonZero;
1598///
1599/// let data: &[u8] = &[
1600///     0x10, 0x00,
1601///     0x10, 0x00,
1602///     0x00, 0x00, 0x0c,
1603///     0x00, 0x00, 0x0c,
1604///     0b00001010, 0b11000100, 0b0100_000_0, 0b1111_0000,
1605///     0b00000000, 0b00000000, 0b00000000, 0b01010000,
1606///     0xf5, 0x3f, 0x86, 0x87, 0x6d, 0xcd, 0x77, 0x83,
1607///     0x22, 0x5c, 0x93, 0xba, 0x8a, 0x93, 0x8c, 0x7d,
1608/// ];
1609///
1610/// let mut r = BitReader::endian(data, BigEndian);
1611/// assert_eq!(
1612///     r.parse::<Streaminfo>().unwrap(),
1613///     Streaminfo {
1614///         minimum_block_size: 0x10_00,                    // 4096 samples
1615///         maximum_block_size: 0x10_00,                    // 4096 samples
1616///         minimum_frame_size: NonZero::new(0x00_00_0c),   // 12 bytes
1617///         maximum_frame_size: NonZero::new(0x00_00_0c),   // 12 bytes
1618///         sample_rate: 0b00001010_11000100_0100,          // 44100 Hz
1619///         channels: NonZero::new(0b000 + 1).unwrap(),     // 1 channel
1620///         bits_per_sample:
1621///             SignedBitCount::new::<{0b0_1111 + 1}>(),    // 16 bps
1622///         total_samples: NonZero::new(
1623///             0b0000_00000000_00000000_00000000_01010000  // 80 samples
1624///         ),
1625///         md5: Some([
1626///             0xf5, 0x3f, 0x86, 0x87, 0x6d, 0xcd, 0x77, 0x83,
1627///             0x22, 0x5c, 0x93, 0xba, 0x8a, 0x93, 0x8c, 0x7d,
1628///         ]),
1629///     },
1630/// );
1631/// ```
1632///
1633/// # Important
1634///
1635/// Changing any of these values to something that differs
1636/// from the values of the file's frame headers will render it
1637/// unplayable, as will moving it anywhere but the first
1638/// metadata block in the file.
1639/// Avoid modifying the position and contents of this block unless you
1640/// know exactly what you are doing.
1641#[derive(Debug, Clone, Eq, PartialEq)]
1642pub struct Streaminfo {
1643    /// The minimum block size (in samples) used in the stream,
1644    /// excluding the last block.
1645    pub minimum_block_size: u16,
1646    /// The maximum block size (in samples) used in the stream,
1647    /// excluding the last block.
1648    pub maximum_block_size: u16,
1649    /// The minimum framesize (in bytes) used in the stream.
1650    ///
1651    /// `None` indicates the value is unknown.
1652    pub minimum_frame_size: Option<NonZero<u32>>,
1653    /// The maximum framesize (in bytes) used in the stream.
1654    ///
1655    /// `None` indicates the value is unknown.
1656    pub maximum_frame_size: Option<NonZero<u32>>,
1657    /// Sample rate in Hz
1658    ///
1659    /// 0 indicates a non-audio stream.
1660    pub sample_rate: u32,
1661    /// Number of channels, from 1 to 8
1662    pub channels: NonZero<u8>,
1663    /// Number of bits-per-sample, from 4 to 32
1664    pub bits_per_sample: SignedBitCount<32>,
1665    /// Total number of interchannel samples in stream.
1666    ///
1667    /// `None` indicates the value is unknown.
1668    pub total_samples: Option<NonZero<u64>>,
1669    /// MD5 hash of unencoded audio data.
1670    ///
1671    /// `None` indicates the value is unknown.
1672    pub md5: Option<[u8; 16]>,
1673}
1674
1675impl Streaminfo {
1676    /// The maximum size of a frame, in bytes (2²⁴ - 1)
1677    pub const MAX_FRAME_SIZE: u32 = (1 << 24) - 1;
1678
1679    /// The maximum sample rate, in Hz (2²⁰ - 1)
1680    pub const MAX_SAMPLE_RATE: u32 = (1 << 20) - 1;
1681
1682    /// The maximum number of channels (8)
1683    pub const MAX_CHANNELS: NonZero<u8> = NonZero::new(8).unwrap();
1684
1685    /// The maximum number of total samples (2³⁶ - 1)
1686    pub const MAX_TOTAL_SAMPLES: NonZero<u64> = NonZero::new((1 << 36) - 1).unwrap();
1687
1688    /// Defined size of STREAMINFO block
1689    const SIZE: BlockSize = BlockSize(0x22);
1690}
1691
1692block!(Streaminfo, Streaminfo, false);
1693
1694impl Metadata for Streaminfo {
1695    fn channel_count(&self) -> u8 {
1696        self.channels.get()
1697    }
1698
1699    fn sample_rate(&self) -> u32 {
1700        self.sample_rate
1701    }
1702
1703    fn bits_per_sample(&self) -> u32 {
1704        self.bits_per_sample.into()
1705    }
1706
1707    fn total_samples(&self) -> Option<u64> {
1708        self.total_samples.map(|s| s.get())
1709    }
1710
1711    fn md5(&self) -> Option<&[u8; 16]> {
1712        self.md5.as_ref()
1713    }
1714}
1715
1716impl FromBitStream for Streaminfo {
1717    type Error = std::io::Error;
1718
1719    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
1720        Ok(Self {
1721            minimum_block_size: r.read_to()?,
1722            maximum_block_size: r.read_to()?,
1723            minimum_frame_size: r.read::<24, _>()?,
1724            maximum_frame_size: r.read::<24, _>()?,
1725            sample_rate: r.read::<20, _>()?,
1726            channels: r.read::<3, _>()?,
1727            bits_per_sample: r
1728                .read_count::<0b11111>()?
1729                .checked_add(1)
1730                .and_then(|c| c.signed_count())
1731                .unwrap(),
1732            total_samples: r.read::<36, _>()?,
1733            md5: r
1734                .read_to()
1735                .map(|md5: [u8; 16]| md5.iter().any(|b| *b != 0).then_some(md5))?,
1736        })
1737    }
1738}
1739
1740impl ToBitStream for Streaminfo {
1741    type Error = std::io::Error;
1742
1743    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
1744        w.write_from(self.minimum_block_size)?;
1745        w.write_from(self.maximum_block_size)?;
1746        w.write::<24, _>(self.minimum_frame_size)?;
1747        w.write::<24, _>(self.maximum_frame_size)?;
1748        w.write::<20, _>(self.sample_rate)?;
1749        w.write::<3, _>(self.channels)?;
1750        w.write_count(
1751            self.bits_per_sample
1752                .checked_sub::<0b11111>(1)
1753                .unwrap()
1754                .count(),
1755        )?;
1756        w.write::<36, _>(self.total_samples)?;
1757        w.write_from(self.md5.unwrap_or([0; 16]))?;
1758        Ok(())
1759    }
1760}
1761
1762/// A PADDING metadata block
1763///
1764/// Padding blocks are empty blocks consisting of all 0 bytes.
1765/// If one wishes to edit the metadata in other blocks,
1766/// adjusting the size of the padding block allows
1767/// us to do so without have to rewrite the entire FLAC file.
1768/// For example, when adding 10 bytes to a comment,
1769/// we can subtract 10 bytes from the padding
1770/// and the total size of all blocks remains unchanged.
1771/// Therefore we can simply overwrite the old comment
1772/// block with the new without affecting the following
1773/// FLAC audio frames.
1774///
1775/// This block may occur multiple times in a FLAC file.
1776///
1777/// # Example
1778///
1779/// ```
1780/// use bitstream_io::{BitReader, BitRead, BigEndian};
1781/// use flac_codec::metadata::{BlockHeader, BlockType, Padding};
1782///
1783/// let data: &[u8] = &[
1784///     0x81, 0x00, 0x00, 0x0a,  // block header
1785///     // padding bytes
1786///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1787/// ];
1788///
1789/// let mut r = BitReader::endian(data, BigEndian);
1790/// let header = r.parse::<BlockHeader>().unwrap();
1791/// assert_eq!(
1792///     &header,
1793///     &BlockHeader {
1794///         last: true,
1795///         block_type: BlockType::Padding,
1796///         size: 0x0au8.into(),
1797///     },
1798/// );
1799///
1800/// assert_eq!(
1801///     r.parse_using::<Padding>(header.size).unwrap(),
1802///     Padding {
1803///         size: 0x0au8.into(),
1804///     },
1805/// );
1806/// ```
1807#[derive(Debug, Clone, Eq, PartialEq, Default)]
1808pub struct Padding {
1809    /// The size of the padding, in bytes
1810    pub size: BlockSize,
1811}
1812
1813block!(Padding, Padding, true);
1814optional_block!(Padding, Padding);
1815
1816impl FromBitStreamUsing for Padding {
1817    type Context = BlockSize;
1818    type Error = Error;
1819
1820    fn from_reader<R: BitRead + ?Sized>(r: &mut R, size: BlockSize) -> Result<Self, Self::Error> {
1821        r.skip(size.get() * 8)?;
1822        Ok(Self { size })
1823    }
1824}
1825
1826impl ToBitStream for Padding {
1827    type Error = std::io::Error;
1828
1829    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
1830        w.pad(self.size.get() * 8)
1831    }
1832}
1833
1834/// An APPLICATION metadata block
1835///
1836/// This block is for handling application-specific binary metadata,
1837/// such as foreign RIFF WAVE tags.
1838///
1839/// This block may occur multiple times in a FLAC file.
1840///
1841/// | Bits | Field | Meaning |
1842/// |-----:|------:|---------|
1843/// | 32   | `id` | registered application ID
1844/// | rest of block | `data` | application-specific data
1845///
1846#[derive(Debug, Clone, Eq, PartialEq)]
1847pub struct Application {
1848    /// A registered application ID
1849    pub id: u32,
1850    /// Application-specific data
1851    pub data: Vec<u8>,
1852}
1853
1854impl Application {
1855    /// Application ID for RIFF chunk storage
1856    pub const RIFF: u32 = 0x72696666;
1857
1858    /// Application ID for AIFF chunk storage
1859    pub const AIFF: u32 = 0x61696666;
1860}
1861
1862block!(Application, Application, true);
1863optional_block!(Application, Application);
1864
1865impl FromBitStreamUsing for Application {
1866    type Context = BlockSize;
1867    type Error = Error;
1868
1869    fn from_reader<R: BitRead + ?Sized>(r: &mut R, size: BlockSize) -> Result<Self, Self::Error> {
1870        Ok(Self {
1871            id: r.read_to()?,
1872            data: r.read_to_vec(
1873                size.get()
1874                    .checked_sub(4)
1875                    .ok_or(Error::InsufficientApplicationBlock)?
1876                    .try_into()
1877                    .unwrap(),
1878            )?,
1879        })
1880    }
1881}
1882
1883impl ToBitStream for Application {
1884    type Error = std::io::Error;
1885
1886    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
1887        w.write_from(self.id)?;
1888        w.write_bytes(&self.data)
1889    }
1890}
1891
1892/// A SEEKTABLE metadata block
1893///
1894/// Because FLAC frames do not store their compressed length,
1895/// a seek table is used for random access within a FLAC file.
1896/// By mapping a sample number to a byte offset,
1897/// one can quickly reach different parts of the file
1898/// without decoding the whole thing.
1899///
1900/// Also note that seek point byte offsets are
1901/// relative to the start of the first FLAC frame,
1902/// and *not* relative to the start of the entire file.
1903/// This allows us to change the size of the set
1904/// of metadata blocks without having to recalculate
1905/// the contents of the seek table.
1906///
1907/// Because the byte and sample offsets are
1908/// file-specific, a seek table generated for one file
1909/// should not be transferred to another FLAC file where the
1910/// frames are different sizes and in different positions.
1911///
1912/// This block may occur only once in a FLAC file.
1913///
1914/// Its seekpoints occupy the entire block.
1915///
1916/// # Example
1917/// ```
1918/// use bitstream_io::{BitReader, BitRead, BigEndian};
1919/// use flac_codec::metadata::{BlockHeader, BlockType, SeekTable, SeekPoint};
1920///
1921/// let data: &[u8] = &[
1922///     0x83, 0x00, 0x00, 0x48,  // block header
1923///     // seekpoint 0
1924///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1925///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1926///     0x00, 0x14,
1927///     // seekpoint 1
1928///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
1929///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
1930///     0x00, 0x14,
1931///     // seekpoint 2
1932///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28,
1933///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22,
1934///     0x00, 0x14,
1935///     // seekpoint 3
1936///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
1937///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
1938///     0x00, 0x14,
1939/// ];
1940///
1941/// let mut r = BitReader::endian(data, BigEndian);
1942/// let header = r.parse::<BlockHeader>().unwrap();
1943/// assert_eq!(
1944///     &header,
1945///     &BlockHeader {
1946///         last: true,
1947///         block_type: BlockType::SeekTable,
1948///         size: 0x48u8.into(),
1949///     },
1950/// );
1951///
1952/// let seektable = r.parse_using::<SeekTable>(header.size).unwrap();
1953///
1954/// assert_eq!(
1955///     Vec::from(seektable.points),
1956///     vec![
1957///         SeekPoint::Defined {
1958///             sample_offset: 0x00,
1959///             byte_offset: 0x00,
1960///             frame_samples: 0x14,
1961///         },
1962///         SeekPoint::Defined {
1963///             sample_offset: 0x14,
1964///             byte_offset: 0x0c,
1965///             frame_samples: 0x14,
1966///         },
1967///         SeekPoint::Defined {
1968///             sample_offset: 0x28,
1969///             byte_offset: 0x22,
1970///             frame_samples: 0x14,
1971///         },
1972///         SeekPoint::Defined {
1973///             sample_offset: 0x3c,
1974///             byte_offset: 0x3c,
1975///             frame_samples: 0x14,
1976///         },
1977///     ],
1978/// );
1979///
1980/// ```
1981#[derive(Debug, Clone, Eq, PartialEq, Default)]
1982pub struct SeekTable {
1983    /// The seek table's individual seek points
1984    pub points: contiguous::Contiguous<{ Self::MAX_POINTS }, SeekPoint>,
1985}
1986
1987impl SeekTable {
1988    /// The maximum number of seek points that fit into a seek table
1989    pub const MAX_POINTS: usize = (1 << 24) / ((64 + 64 + 16) / 8);
1990}
1991
1992block!(SeekTable, SeekTable, false);
1993optional_block!(SeekTable, SeekTable);
1994
1995impl FromBitStreamUsing for SeekTable {
1996    type Context = BlockSize;
1997    type Error = Error;
1998
1999    fn from_reader<R: BitRead + ?Sized>(r: &mut R, size: BlockSize) -> Result<Self, Self::Error> {
2000        match (size.get() / 18, size.get() % 18) {
2001            (p, 0) => Ok(Self {
2002                points: contiguous::Contiguous::try_collect((0..p).map(|_| r.parse()))
2003                    .map_err(|_| Error::InvalidSeekTablePoint)??,
2004            }),
2005            _ => Err(Error::InvalidSeekTableSize),
2006        }
2007    }
2008}
2009
2010impl ToBitStream for SeekTable {
2011    type Error = Error;
2012
2013    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
2014        // ensure non-placeholder seek point offsets increment
2015        let mut last_offset = None;
2016
2017        self.points
2018            .iter()
2019            .try_for_each(|point| match last_offset.as_mut() {
2020                None => {
2021                    last_offset = point.sample_offset();
2022                    w.build(point).map_err(Error::Io)
2023                }
2024                Some(last_offset) => match point.sample_offset() {
2025                    Some(our_offset) => match our_offset > *last_offset {
2026                        true => {
2027                            *last_offset = our_offset;
2028                            w.build(point).map_err(Error::Io)
2029                        }
2030                        false => Err(Error::InvalidSeekTablePoint),
2031                    },
2032                    _ => w.build(point).map_err(Error::Io),
2033                },
2034            })
2035    }
2036}
2037
2038/// An individual SEEKTABLE seek point
2039///
2040/// | Bits | Field | Meaning |
2041/// |-----:|------:|---------|
2042/// | 64   | `sample_offset` | sample number of first sample in target frame
2043/// | 64   | `byte_offset` | offset, in bytes, from first frame to target frame's header
2044/// | 16   | `frame_samples` | number of samples in target frame
2045///
2046#[derive(Debug, Clone, Eq, PartialEq)]
2047pub enum SeekPoint {
2048    /// A defined, non-placeholder seek point
2049    Defined {
2050        /// The sample number of the first sample in the target frame
2051        sample_offset: u64,
2052        /// Offset, in bytes, from the first byte of the first frame header
2053        /// to the first byte in the target frame's header
2054        byte_offset: u64,
2055        /// Number of samples in the target frame
2056        frame_samples: u16,
2057    },
2058    /// A placeholder seek point
2059    Placeholder,
2060}
2061
2062impl SeekPoint {
2063    /// Returns our sample offset, if not a placeholder point
2064    pub fn sample_offset(&self) -> Option<u64> {
2065        match self {
2066            Self::Defined { sample_offset, .. } => Some(*sample_offset),
2067            Self::Placeholder => None,
2068        }
2069    }
2070}
2071
2072impl contiguous::Adjacent for SeekPoint {
2073    fn valid_first(&self) -> bool {
2074        true
2075    }
2076
2077    fn is_next(&self, previous: &SeekPoint) -> bool {
2078        // seekpoints must be unique by sample offset
2079        // and sample offsets must be ascending
2080        //
2081        // placeholders can come after non-placeholders or other placeholders,
2082        // but non-placeholders can't come after placeholders
2083        match self {
2084            Self::Defined {
2085                sample_offset: our_offset,
2086                ..
2087            } => match previous {
2088                Self::Defined {
2089                    sample_offset: prev_offset,
2090                    ..
2091                } => our_offset > prev_offset,
2092                Self::Placeholder => false,
2093            },
2094            Self::Placeholder => true,
2095        }
2096    }
2097}
2098
2099impl FromBitStream for SeekPoint {
2100    type Error = std::io::Error;
2101
2102    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
2103        match r.read_to()? {
2104            u64::MAX => {
2105                let _byte_offset = r.read_to::<u64>()?;
2106                let _frame_samples = r.read_to::<u16>()?;
2107                Ok(Self::Placeholder)
2108            }
2109            sample_offset => Ok(Self::Defined {
2110                sample_offset,
2111                byte_offset: r.read_to()?,
2112                frame_samples: r.read_to()?,
2113            }),
2114        }
2115    }
2116}
2117
2118impl ToBitStream for SeekPoint {
2119    type Error = std::io::Error;
2120
2121    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
2122        match self {
2123            Self::Defined {
2124                sample_offset,
2125                byte_offset,
2126                frame_samples,
2127            } => {
2128                w.write_from(*sample_offset)?;
2129                w.write_from(*byte_offset)?;
2130                w.write_from(*frame_samples)
2131            }
2132            Self::Placeholder => {
2133                w.write_from(u64::MAX)?;
2134                w.write_from::<u64>(0)?;
2135                w.write_from::<u16>(0)
2136            }
2137        }
2138    }
2139}
2140
2141/// A VORBIS_COMMENT metadata block
2142///
2143/// This block contains metadata such as track name,
2144/// artist name, album name, etc.  Its contents are
2145/// UTF-8 encoded, `=`-delimited text fields
2146/// with a field name followed by value,
2147/// such as:
2148///
2149/// ```text
2150/// TITLE=Track Title
2151/// ```
2152///
2153/// Field names are case-insensitive and
2154/// may occur multiple times within the same comment
2155/// (a track may have multiple artists and choose to
2156/// store an "ARTIST" field for each one).
2157///
2158/// Commonly-used fields are available in the [`fields`] module.
2159///
2160/// This block may occur only once in a FLAC file.
2161///
2162/// # Byte Order
2163///
2164/// Unlike the rest of a FLAC file, the Vorbis comment's
2165/// length fields are stored in little-endian byte order.
2166///
2167/// | Bits | Field | Meaning |
2168/// |-----:|------:|---------|
2169/// | 32   | vendor string len | length of vendor string, in bytes
2170/// | `vendor string len`×8 | `vendor_string` | vendor string, in UTF-8
2171/// | 32   | field count | number of vendor string fields
2172/// | 32   | field₀ len | length of field₀, in bytes
2173/// | `field₀ len`×8 | `fields₀` | first field value, in UTF-8
2174/// | 32   | field₁ len | length of field₁, in bytes
2175/// | `field₁ len`×8 | `fields₁` | second field value, in UTF-8
2176/// | | | ⋮
2177///
2178/// # Example
2179/// ```
2180/// use bitstream_io::{BitReader, BitRead, LittleEndian};
2181/// use flac_codec::metadata::VorbisComment;
2182/// use flac_codec::metadata::fields::{TITLE, ALBUM, ARTIST};
2183///
2184/// let data: &[u8] = &[
2185///     0x20, 0x00, 0x00, 0x00,  // 32 byte vendor string
2186///     0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
2187///     0x65, 0x20, 0x6c, 0x69, 0x62, 0x46, 0x4c, 0x41,
2188///     0x43, 0x20, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x20,
2189///     0x32, 0x30, 0x32, 0x33, 0x30, 0x36, 0x32, 0x33,
2190///     0x02, 0x00, 0x00, 0x00,  // 2 fields
2191///     0x0d, 0x00, 0x00, 0x00,  // 13 byte field 1
2192///     0x54, 0x49, 0x54, 0x4c, 0x45, 0x3d, 0x54, 0x65,
2193///     0x73, 0x74, 0x69, 0x6e, 0x67,
2194///     0x10, 0x00, 0x00, 0x00,  // 16 byte field 2
2195///     0x41, 0x4c, 0x42, 0x55, 0x4d, 0x3d, 0x54, 0x65,
2196///     0x73, 0x74, 0x20, 0x41, 0x6c, 0x62, 0x75, 0x6d,
2197/// ];
2198///
2199/// let mut r = BitReader::endian(data, LittleEndian);
2200/// let comment = r.parse::<VorbisComment>().unwrap();
2201///
2202/// assert_eq!(
2203///     &comment,
2204///     &VorbisComment {
2205///         vendor_string: "reference libFLAC 1.4.3 20230623".to_string(),
2206///         fields: vec![
2207///              "TITLE=Testing".to_string(),
2208///              "ALBUM=Test Album".to_string(),
2209///         ],
2210///     },
2211/// );
2212///
2213/// assert_eq!(comment.get(TITLE), Some("Testing"));
2214/// assert_eq!(comment.get(ALBUM), Some("Test Album"));
2215/// assert_eq!(comment.get(ARTIST), None);
2216/// ```
2217#[derive(Debug, Clone, Eq, PartialEq)]
2218pub struct VorbisComment {
2219    /// The vendor string
2220    pub vendor_string: String,
2221    /// The individual metadata comment strings
2222    pub fields: Vec<String>,
2223}
2224
2225impl Default for VorbisComment {
2226    fn default() -> Self {
2227        Self {
2228            vendor_string: concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION"))
2229                .to_owned(),
2230            fields: vec![],
2231        }
2232    }
2233}
2234
2235impl VorbisComment {
2236    /// Given a field name, returns first matching value, if any
2237    ///
2238    /// Fields are matched case-insensitively
2239    ///
2240    /// # Example
2241    ///
2242    /// ```
2243    /// use flac_codec::metadata::{VorbisComment, fields::{ARTIST, TITLE}};
2244    ///
2245    /// let comment = VorbisComment {
2246    ///     fields: vec![
2247    ///         "ARTIST=Artist 1".to_owned(),
2248    ///         "ARTIST=Artist 2".to_owned(),
2249    ///     ],
2250    ///     ..VorbisComment::default()
2251    /// };
2252    ///
2253    /// assert_eq!(comment.get(ARTIST), Some("Artist 1"));
2254    /// assert_eq!(comment.get(TITLE), None);
2255    /// ```
2256    pub fn get(&self, field: &str) -> Option<&str> {
2257        self.all(field).next()
2258    }
2259
2260    /// Replaces any instances of the given field with value
2261    ///
2262    /// Fields are matched case-insensitively
2263    ///
2264    /// # Panics
2265    ///
2266    /// Panics if field contains the `=` character.
2267    ///
2268    /// # Example
2269    ///
2270    /// ```
2271    /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2272    ///
2273    /// let mut comment = VorbisComment {
2274    ///     fields: vec![
2275    ///         "ARTIST=Artist 1".to_owned(),
2276    ///         "ARTIST=Artist 2".to_owned(),
2277    ///     ],
2278    ///     ..VorbisComment::default()
2279    /// };
2280    ///
2281    /// comment.set(ARTIST, "Artist 3");
2282    ///
2283    /// assert_eq!(
2284    ///     comment.all(ARTIST).collect::<Vec<_>>(),
2285    ///     vec!["Artist 3"],
2286    /// );
2287    /// ```
2288    pub fn set<S>(&mut self, field: &str, value: S)
2289    where
2290        S: std::fmt::Display,
2291    {
2292        self.remove(field);
2293        self.insert(field, value);
2294    }
2295
2296    /// Given a field name, iterates over any matching values
2297    ///
2298    /// Fields are matched case-insensitively
2299    ///
2300    /// # Example
2301    ///
2302    /// ```
2303    /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2304    ///
2305    /// let comment = VorbisComment {
2306    ///     fields: vec![
2307    ///         "ARTIST=Artist 1".to_owned(),
2308    ///         "ARTIST=Artist 2".to_owned(),
2309    ///     ],
2310    ///     ..VorbisComment::default()
2311    /// };
2312    ///
2313    /// assert_eq!(
2314    ///     comment.all(ARTIST).collect::<Vec<_>>(),
2315    ///     vec!["Artist 1", "Artist 2"],
2316    /// );
2317    /// ```
2318    pub fn all(&self, field: &str) -> impl Iterator<Item = &str> {
2319        assert!(!field.contains('='), "field must not contain '='");
2320
2321        self.fields.iter().filter_map(|f| {
2322            f.split_once('=')
2323                .and_then(|(key, value)| key.eq_ignore_ascii_case(field).then_some(value))
2324        })
2325    }
2326
2327    /// Adds new instance of field with the given value
2328    ///
2329    /// # Panics
2330    ///
2331    /// Panics if field contains the `=` character.
2332    ///
2333    /// # Example
2334    ///
2335    /// ```
2336    /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2337    ///
2338    /// let mut comment = VorbisComment {
2339    ///     fields: vec![
2340    ///         "ARTIST=Artist 1".to_owned(),
2341    ///         "ARTIST=Artist 2".to_owned(),
2342    ///     ],
2343    ///     ..VorbisComment::default()
2344    /// };
2345    ///
2346    /// comment.insert(ARTIST, "Artist 3");
2347    ///
2348    /// assert_eq!(
2349    ///     comment.all(ARTIST).collect::<Vec<_>>(),
2350    ///     vec!["Artist 1", "Artist 2", "Artist 3"],
2351    /// );
2352    /// ```
2353    pub fn insert<S>(&mut self, field: &str, value: S)
2354    where
2355        S: std::fmt::Display,
2356    {
2357        assert!(!field.contains('='), "field must not contain '='");
2358
2359        self.fields.push(format!("{field}={value}"));
2360    }
2361
2362    /// Removes any matching instances of the given field
2363    ///
2364    /// Fields are matched case-insensitively
2365    ///
2366    /// # Panics
2367    ///
2368    /// Panics if field contains the `=` character.
2369    ///
2370    /// # Example
2371    ///
2372    /// ```
2373    /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2374    ///
2375    /// let mut comment = VorbisComment {
2376    ///     fields: vec![
2377    ///         "ARTIST=Artist 1".to_owned(),
2378    ///         "ARTIST=Artist 2".to_owned(),
2379    ///     ],
2380    ///     ..VorbisComment::default()
2381    /// };
2382    ///
2383    /// comment.remove(ARTIST);
2384    ///
2385    /// assert_eq!(comment.get(ARTIST), None);
2386    /// ```
2387    pub fn remove(&mut self, field: &str) {
2388        assert!(!field.contains('='), "field must not contain '='");
2389
2390        self.fields.retain(|f| match f.split_once('=') {
2391            Some((key, _)) => !key.eq_ignore_ascii_case(field),
2392            None => true,
2393        });
2394    }
2395
2396    /// Replaces any instances of the given field with the given values
2397    ///
2398    /// Fields are matched case-insensitively
2399    ///
2400    /// # Panics
2401    ///
2402    /// Panics if field contains the `=` character
2403    ///
2404    /// # Example
2405    ///
2406    /// ```
2407    /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2408    ///
2409    /// let mut comment = VorbisComment {
2410    ///     fields: vec![
2411    ///         "ARTIST=Artist 1".to_owned(),
2412    ///         "ARTIST=Artist 2".to_owned(),
2413    ///     ],
2414    ///     ..VorbisComment::default()
2415    /// };
2416    ///
2417    /// comment.replace(ARTIST, ["Artist 3", "Artist 4"]);
2418    ///
2419    /// assert_eq!(
2420    ///     comment.all(ARTIST).collect::<Vec<_>>(),
2421    ///     vec!["Artist 3", "Artist 4"],
2422    /// );
2423    ///
2424    /// // reminder that Option also implements IntoIterator
2425    /// comment.replace(ARTIST, Some("Artist 5"));
2426    ///
2427    /// assert_eq!(
2428    ///     comment.all(ARTIST).collect::<Vec<_>>(),
2429    ///     vec!["Artist 5"],
2430    /// );
2431    /// ```
2432    pub fn replace<S: std::fmt::Display>(
2433        &mut self,
2434        field: &str,
2435        replacements: impl IntoIterator<Item = S>,
2436    ) {
2437        self.remove(field);
2438        self.fields.extend(
2439            replacements
2440                .into_iter()
2441                .map(|value| format!("{field}={value}")),
2442        );
2443    }
2444
2445    /// Replaces instances of the given field with a closure's result
2446    ///
2447    /// Closure takes the field's current value and returns
2448    /// something `Display`-able such as a `String`.
2449    ///
2450    /// The closure is called for each maching field, if any.
2451    ///
2452    /// Fields are matched case-insensitively.
2453    ///
2454    /// # Panics
2455    ///
2456    /// Panics if field contains the `=` character
2457    ///
2458    /// # Example
2459    ///
2460    /// ```
2461    /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2462    ///
2463    /// let mut comment = VorbisComment {
2464    ///     fields: vec![
2465    ///         "ARTIST=some artist".to_owned(),
2466    ///     ],
2467    ///     ..VorbisComment::default()
2468    /// };
2469    ///
2470    /// comment.replace_with(ARTIST, |s| s.to_ascii_uppercase());
2471    ///
2472    /// assert_eq!(comment.get(ARTIST), Some("SOME ARTIST"));
2473    /// ```
2474    pub fn replace_with<S: std::fmt::Display>(
2475        &mut self,
2476        field: &str,
2477        mut f: impl FnMut(&str) -> S,
2478    ) {
2479        assert!(!field.contains('='), "field must not contain '='");
2480
2481        self.fields.iter_mut().for_each(|s| {
2482            if let Some((key, value)) = s.split_once('=')
2483                && key.eq_ignore_ascii_case(field)
2484            {
2485                *s = format!("{key}={}", f(value));
2486            }
2487        })
2488    }
2489}
2490
2491block!(VorbisComment, VorbisComment, false);
2492optional_block!(VorbisComment, VorbisComment);
2493
2494impl FromBitStream for VorbisComment {
2495    type Error = Error;
2496
2497    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
2498        fn read_string<R: BitRead + ?Sized>(r: &mut R) -> Result<String, Error> {
2499            let size = r.read_as_to::<LittleEndian, u32>()?.try_into().unwrap();
2500            Ok(String::from_utf8(r.read_to_vec(size)?)?)
2501        }
2502
2503        Ok(Self {
2504            vendor_string: read_string(r)?,
2505            fields: (0..(r.read_as_to::<LittleEndian, u32>()?))
2506                .map(|_| read_string(r))
2507                .collect::<Result<Vec<_>, _>>()?,
2508        })
2509    }
2510}
2511
2512impl ToBitStream for VorbisComment {
2513    type Error = Error;
2514
2515    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
2516        fn write_string<W: BitWrite + ?Sized>(w: &mut W, s: &str) -> Result<(), Error> {
2517            w.write_as_from::<LittleEndian, u32>(
2518                s.len()
2519                    .try_into()
2520                    .map_err(|_| Error::ExcessiveStringLength)?,
2521            )?;
2522            w.write_bytes(s.as_bytes())?;
2523            Ok(())
2524        }
2525
2526        write_string(w, &self.vendor_string)?;
2527        w.write_as_from::<LittleEndian, u32>(
2528            self.fields
2529                .len()
2530                .try_into()
2531                .map_err(|_| Error::ExcessiveVorbisEntries)?,
2532        )?;
2533        self.fields.iter().try_for_each(|s| write_string(w, s))
2534    }
2535}
2536
2537// As neat as it might be implement IndexMut for VorbisComment,
2538// the trait simply isn't compatible.  A "&mut str" is mostly
2539// useless, and I can't return a partial "&mut String"
2540// in order to assing a new string to everything after the
2541// initial "FIELD=" indicator.
2542
2543/// Vorbis comment metadata tag fields
2544///
2545/// Not all of these fields are officially defined in the specification,
2546/// but they are in common use.
2547pub mod fields {
2548    /// Name of current work
2549    pub const TITLE: &str = "TITLE";
2550
2551    /// Name of the artist generally responsible for the current work
2552    pub const ARTIST: &str = "ARTIST";
2553
2554    /// Name of the collection the current work belongs to
2555    pub const ALBUM: &str = "ALBUM";
2556
2557    /// The work's original composer
2558    pub const COMPOSER: &str = "COMPOSER";
2559
2560    /// The performance's conductor
2561    pub const CONDUCTOR: &str = "CONDUCTOR";
2562
2563    /// The current work's performer(s)
2564    pub const PERFORMER: &str = "PERFORMER";
2565
2566    /// The album's publisher
2567    pub const PUBLISHER: &str = "PUBLISHER";
2568
2569    /// The album's catalog number
2570    pub const CATALOG: &str = "CATALOG";
2571
2572    /// Release date of work
2573    pub const DATE: &str = "DATE";
2574
2575    /// Generic comment
2576    pub const COMMENT: &str = "COMMENT";
2577
2578    /// Track number in album
2579    pub const TRACK_NUMBER: &str = "TRACKNUMBER";
2580
2581    /// Total tracks in album
2582    pub const TRACK_TOTAL: &str = "TRACKTOTAL";
2583
2584    /// The channel mask of multi-channel audio streams
2585    pub const CHANNEL_MASK: &str = "WAVEFORMATEXTENSIBLE_CHANNEL_MASK";
2586
2587    /// ReplayGain track gain
2588    pub const RG_TRACK_GAIN: &str = "REPLAYGAIN_TRACK_GAIN";
2589
2590    /// ReplayGain album gain
2591    pub const RG_ALBUM_GAIN: &str = "REPLAYGAIN_ALBUM_GAIN";
2592
2593    /// ReplayGain track peak
2594    pub const RG_TRACK_PEAK: &str = "REPLAYGAIN_TRACK_PEAK";
2595
2596    /// ReplayGain album peak
2597    pub const RG_ALBUM_PEAK: &str = "REPLAYGAIN_ALBUM_PEAK";
2598
2599    /// ReplayGain reference loudness
2600    pub const RG_REFERENCE_LOUDNESS: &str = "REPLAYGAIN_REFERENCE_LOUDNESS";
2601}
2602
2603/// Types for collections which must be contiguous
2604///
2605/// Used by the SEEKTABLE and CUESHEET metadata blocks
2606pub mod contiguous {
2607    /// A trait for types which can be contiguous
2608    pub trait Adjacent {
2609        /// Whether the item is valid as the first in a sequence
2610        fn valid_first(&self) -> bool;
2611
2612        /// Whether the item is immediately following the previous
2613        fn is_next(&self, previous: &Self) -> bool;
2614    }
2615
2616    impl Adjacent for u64 {
2617        fn valid_first(&self) -> bool {
2618            *self == 0
2619        }
2620
2621        fn is_next(&self, previous: &Self) -> bool {
2622            *self > *previous
2623        }
2624    }
2625
2626    impl Adjacent for std::num::NonZero<u8> {
2627        fn valid_first(&self) -> bool {
2628            *self == Self::MIN
2629        }
2630
2631        fn is_next(&self, previous: &Self) -> bool {
2632            previous.checked_add(1).map(|n| n == *self).unwrap_or(false)
2633        }
2634    }
2635
2636    /// A Vec-like type which requires all items to be adjacent
2637    #[derive(Debug, Clone, Eq, PartialEq)]
2638    pub struct Contiguous<const MAX: usize, T: Adjacent> {
2639        items: Vec<T>,
2640    }
2641
2642    impl<const MAX: usize, T: Adjacent> Default for Contiguous<MAX, T> {
2643        fn default() -> Self {
2644            Self { items: Vec::new() }
2645        }
2646    }
2647
2648    impl<const MAX: usize, T: Adjacent> Contiguous<MAX, T> {
2649        /// Constructs new contiguous block with the given capacity
2650        ///
2651        /// See [`Vec::with_capacity`]
2652        pub fn with_capacity(capacity: usize) -> Self {
2653            Self {
2654                items: Vec::with_capacity(capacity.min(MAX)),
2655            }
2656        }
2657
2658        /// Removes all items without adjust total capacity
2659        ///
2660        /// See [`Vec::clear`]
2661        pub fn clear(&mut self) {
2662            self.items.clear()
2663        }
2664
2665        /// Attempts to push item into contiguous set
2666        ///
2667        /// # Errors
2668        ///
2669        /// Returns error if item is not a valid first item
2670        /// in the set or is not contiguous with the
2671        /// existing items.
2672        pub fn try_push(&mut self, item: T) -> Result<(), NonContiguous> {
2673            if self.items.len() < MAX {
2674                if match self.items.last() {
2675                    None => item.valid_first(),
2676                    Some(last) => item.is_next(last),
2677                } {
2678                    self.items.push(item);
2679                    Ok(())
2680                } else {
2681                    Err(NonContiguous)
2682                }
2683            } else {
2684                Err(NonContiguous)
2685            }
2686        }
2687
2688        /// Attempts to extends set with items from iterator
2689        pub fn try_extend(
2690            &mut self,
2691            iter: impl IntoIterator<Item = T>,
2692        ) -> Result<(), NonContiguous> {
2693            iter.into_iter().try_for_each(|item| self.try_push(item))
2694        }
2695
2696        /// Attempts to collect a contiguous set from a fallible iterator
2697        pub fn try_collect<I, E>(iter: I) -> Result<Result<Self, E>, NonContiguous>
2698        where
2699            I: IntoIterator<Item = Result<T, E>>,
2700        {
2701            let iter = iter.into_iter();
2702            let mut c = Self::with_capacity(iter.size_hint().0);
2703
2704            for item in iter {
2705                match item {
2706                    Ok(item) => c.try_push(item)?,
2707                    Err(err) => return Ok(Err(err)),
2708                }
2709            }
2710            Ok(Ok(c))
2711        }
2712    }
2713
2714    impl<const MAX: usize, T: Adjacent> std::ops::Deref for Contiguous<MAX, T> {
2715        type Target = [T];
2716
2717        fn deref(&self) -> &[T] {
2718            self.items.as_slice()
2719        }
2720    }
2721
2722    impl<const MAX: usize, T: Adjacent> From<Contiguous<MAX, T>> for Vec<T> {
2723        fn from(contiguous: Contiguous<MAX, T>) -> Self {
2724            contiguous.items
2725        }
2726    }
2727
2728    impl<const MAX: usize, T: Adjacent> From<Contiguous<MAX, T>> for std::collections::VecDeque<T> {
2729        fn from(contiguous: Contiguous<MAX, T>) -> Self {
2730            contiguous.items.into()
2731        }
2732    }
2733
2734    impl<const MAX: usize, T: Adjacent> TryFrom<Vec<T>> for Contiguous<MAX, T> {
2735        type Error = NonContiguous;
2736
2737        fn try_from(items: Vec<T>) -> Result<Self, Self::Error> {
2738            (items.len() <= MAX && is_contiguous(&items))
2739                .then_some(Self { items })
2740                .ok_or(NonContiguous)
2741        }
2742    }
2743
2744    /// Attempted to insert a non-contiguous item into a set
2745    #[derive(Copy, Clone, Debug)]
2746    pub struct NonContiguous;
2747
2748    impl std::error::Error for NonContiguous {}
2749
2750    impl std::fmt::Display for NonContiguous {
2751        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2752            "item is non-contiguous".fmt(f)
2753        }
2754    }
2755
2756    fn is_contiguous<'t, T: Adjacent + 't>(items: impl IntoIterator<Item = &'t T>) -> bool {
2757        and_previous(items).all(|(prev, item)| match prev {
2758            Some(prev) => item.is_next(prev),
2759            None => item.valid_first(),
2760        })
2761    }
2762
2763    fn and_previous<T: Copy>(
2764        iter: impl IntoIterator<Item = T>,
2765    ) -> impl Iterator<Item = (Option<T>, T)> {
2766        let mut previous = None;
2767        iter.into_iter().map(move |i| (previous.replace(i), i))
2768    }
2769}
2770
2771/// A CUESHEET metadata block
2772///
2773/// A cue sheet stores a disc's original layout
2774/// with all its tracks, index points, and disc-specific metadata.
2775///
2776/// This block may occur multiple times in a FLAC file, theoretically.
2777///
2778/// | Bits  | Field | Meaning |
2779/// |------:|------:|---------|
2780/// | 128×8 | `catalog_number` | media catalog number, in ASCII
2781/// | 64 | `lead_in_samples` | number of lead-in samples
2782/// | 1  | `is_cdda` | whether cuesheet corresponds to CD-DA
2783/// | 7+258×8 | padding | all 0 bits
2784/// | 8  | track count | number of cuesheet tracks
2785/// | | `tracks` | cuesheet track₀, cuesheet track₁, …
2786///
2787/// Although the structure of this block is not particularly
2788/// complicated, a CUESHEET block must abide by many rules
2789/// in order to be considered valid.  Many of these
2790/// rules are encoded into the type system.
2791#[derive(Debug, Clone, Eq, PartialEq)]
2792pub enum Cuesheet {
2793    /// A CD-DA Cuesheet, for audio CDs
2794    CDDA {
2795        /// Media catalog number in ASCII digits
2796        ///
2797        /// For CD-DA, if present, this number must
2798        /// be exactly 13 ASCII digits followed by all
2799        /// 0 bytes.
2800        catalog_number: Option<[cuesheet::Digit; 13]>,
2801
2802        /// Number of lead-in samples
2803        ///
2804        /// For CD-DA, this must be at least 2 seconds
2805        /// (88200 samples), but may be longer.
2806        ///
2807        /// Non-CD-DA cuesheets must always use 0 for
2808        /// lead-in samples, which is why that variant
2809        /// does not have this field.
2810        lead_in_samples: u64,
2811
2812        /// The cue sheet's non-lead-out tracks
2813        ///
2814        /// For CD-DA, 0 ≤ track count ≤ 99
2815        tracks: contiguous::Contiguous<99, cuesheet::TrackCDDA>,
2816
2817        /// The required lead-out-track
2818        ///
2819        /// This has a track number of 170 and
2820        /// indicates the end of the disc.
2821        lead_out: cuesheet::LeadOutCDDA,
2822    },
2823    /// A Non-CD-DA Cuesheet, for non-audio CDs
2824    NonCDDA {
2825        /// Media catalog number in ASCII digits
2826        ///
2827        /// 0 ≤ catalog number digits < 120
2828        catalog_number: Vec<cuesheet::Digit>,
2829
2830        /// The cue sheet's non-lead-out tracks
2831        ///
2832        /// For Non-CD-DA, 0 ≤ track count ≤ 254
2833        tracks: contiguous::Contiguous<254, cuesheet::TrackNonCDDA>,
2834
2835        /// The required lead-out-track
2836        ///
2837        /// This has a track number of 255 and
2838        /// indicates the end of the disc.
2839        lead_out: cuesheet::LeadOutNonCDDA,
2840    },
2841}
2842
2843impl Cuesheet {
2844    /// Default number of lead-in samples
2845    const LEAD_IN: u64 = 44100 * 2;
2846
2847    /// Maximum catalog number length, in digits
2848    const CATALOG_LEN: usize = 128;
2849
2850    /// Media catalog number
2851    pub fn catalog_number(&self) -> impl std::fmt::Display {
2852        use cuesheet::Digit;
2853
2854        enum Digits<'d> {
2855            Digits(&'d [Digit]),
2856            Empty,
2857        }
2858
2859        impl std::fmt::Display for Digits<'_> {
2860            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2861                match self {
2862                    Self::Digits(digits) => digits.iter().try_for_each(|d| d.fmt(f)),
2863                    Self::Empty => Ok(()),
2864                }
2865            }
2866        }
2867
2868        match self {
2869            Self::CDDA {
2870                catalog_number: Some(catalog_number),
2871                ..
2872            } => Digits::Digits(catalog_number),
2873            Self::CDDA {
2874                catalog_number: None,
2875                ..
2876            } => Digits::Empty,
2877            Self::NonCDDA { catalog_number, .. } => Digits::Digits(catalog_number),
2878        }
2879    }
2880
2881    /// The number of lead-in samples for CD-DA discs
2882    pub fn lead_in_samples(&self) -> Option<u64> {
2883        match self {
2884            Self::CDDA {
2885                lead_in_samples, ..
2886            } => Some(*lead_in_samples),
2887            Self::NonCDDA { .. } => None,
2888        }
2889    }
2890
2891    /// If this is a CD-DA cuesheet
2892    pub fn is_cdda(&self) -> bool {
2893        matches!(self, Self::CDDA { .. })
2894    }
2895
2896    /// Returns total number of tracks in cuesheet
2897    pub fn track_count(&self) -> usize {
2898        match self {
2899            Self::CDDA { tracks, .. } => tracks.len() + 1,
2900            Self::NonCDDA { tracks, .. } => tracks.len() + 1,
2901        }
2902    }
2903
2904    /// Iterates over all tracks in cuesheet
2905    ///
2906    /// Tracks are converted into a unified format suitable for display
2907    pub fn tracks(&self) -> Box<dyn Iterator<Item = cuesheet::TrackGeneric> + '_> {
2908        use cuesheet::{Index, Track};
2909
2910        match self {
2911            Self::CDDA {
2912                tracks, lead_out, ..
2913            } => Box::new(
2914                tracks
2915                    .iter()
2916                    .map(|track| Track {
2917                        offset: track.offset.into(),
2918                        number: Some(track.number.get()),
2919                        isrc: track.isrc.clone(),
2920                        non_audio: track.non_audio,
2921                        pre_emphasis: track.pre_emphasis,
2922                        index_points: track
2923                            .index_points
2924                            .iter()
2925                            .map(|point| Index {
2926                                number: point.number,
2927                                offset: point.offset.into(),
2928                            })
2929                            .collect(),
2930                    })
2931                    .chain(std::iter::once(Track {
2932                        offset: lead_out.offset.into(),
2933                        number: None,
2934                        isrc: lead_out.isrc.clone(),
2935                        non_audio: lead_out.non_audio,
2936                        pre_emphasis: lead_out.pre_emphasis,
2937                        index_points: vec![],
2938                    })),
2939            ),
2940            Self::NonCDDA {
2941                tracks, lead_out, ..
2942            } => Box::new(
2943                tracks
2944                    .iter()
2945                    .map(|track| Track {
2946                        offset: track.offset,
2947                        number: Some(track.number.get()),
2948                        isrc: track.isrc.clone(),
2949                        non_audio: track.non_audio,
2950                        pre_emphasis: track.pre_emphasis,
2951                        index_points: track
2952                            .index_points
2953                            .iter()
2954                            .map(|point| Index {
2955                                number: point.number,
2956                                offset: point.offset,
2957                            })
2958                            .collect(),
2959                    })
2960                    .chain(std::iter::once(Track {
2961                        offset: lead_out.offset,
2962                        number: None,
2963                        isrc: lead_out.isrc.clone(),
2964                        non_audio: lead_out.non_audio,
2965                        pre_emphasis: lead_out.pre_emphasis,
2966                        index_points: vec![],
2967                    })),
2968            ),
2969        }
2970    }
2971
2972    /// Given a filename to use, returns cuesheet data as text
2973    pub fn display(&self, filename: &str) -> impl std::fmt::Display {
2974        struct DisplayCuesheet<'c, 'f> {
2975            cuesheet: &'c Cuesheet,
2976            filename: &'f str,
2977        }
2978
2979        impl std::fmt::Display for DisplayCuesheet<'_, '_> {
2980            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2981                /// A CUESHEET timestamp in MM:SS:FF format
2982                #[derive(Copy, Clone)]
2983                pub struct Timestamp {
2984                    minutes: u64,
2985                    seconds: u8,
2986                    frames: u8,
2987                }
2988
2989                impl Timestamp {
2990                    const FRAMES_PER_SECOND: u64 = 75;
2991                    const SECONDS_PER_MINUTE: u64 = 60;
2992                    const SAMPLES_PER_FRAME: u64 = 44100 / 75;
2993                }
2994
2995                impl From<u64> for Timestamp {
2996                    fn from(offset: u64) -> Self {
2997                        let total_frames = offset / Self::SAMPLES_PER_FRAME;
2998
2999                        Self {
3000                            minutes: (total_frames / Self::FRAMES_PER_SECOND)
3001                                / Self::SECONDS_PER_MINUTE,
3002                            seconds: ((total_frames / Self::FRAMES_PER_SECOND)
3003                                % Self::SECONDS_PER_MINUTE)
3004                                .try_into()
3005                                .unwrap(),
3006                            frames: (total_frames % Self::FRAMES_PER_SECOND).try_into().unwrap(),
3007                        }
3008                    }
3009                }
3010
3011                impl std::fmt::Display for Timestamp {
3012                    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3013                        write!(
3014                            f,
3015                            "{:02}:{:02}:{:02}",
3016                            self.minutes, self.seconds, self.frames
3017                        )
3018                    }
3019                }
3020
3021                writeln!(f, "FILE \"{}\" FLAC", self.filename)?;
3022
3023                match self.cuesheet {
3024                    Cuesheet::CDDA { tracks, .. } => {
3025                        for track in tracks.iter() {
3026                            writeln!(
3027                                f,
3028                                "  TRACK {} {}",
3029                                track.number,
3030                                if track.non_audio {
3031                                    "NON_AUDIO"
3032                                } else {
3033                                    "AUDIO"
3034                                }
3035                            )?;
3036                            for index in track.index_points.iter() {
3037                                writeln!(
3038                                    f,
3039                                    "    INDEX {:02} {}",
3040                                    index.number,
3041                                    Timestamp::from(u64::from(index.offset + track.offset)),
3042                                )?;
3043                            }
3044                        }
3045                    }
3046                    Cuesheet::NonCDDA { tracks, .. } => {
3047                        for track in tracks.iter() {
3048                            writeln!(
3049                                f,
3050                                "  TRACK {} {}",
3051                                track.number,
3052                                if track.non_audio {
3053                                    "NON_AUDIO"
3054                                } else {
3055                                    "AUDIO"
3056                                }
3057                            )?;
3058                            for index in track.index_points.iter() {
3059                                writeln!(
3060                                    f,
3061                                    "    INDEX {:02} {}",
3062                                    index.number,
3063                                    Timestamp::from(index.offset + track.offset),
3064                                )?;
3065                            }
3066                        }
3067                    }
3068                }
3069
3070                Ok(())
3071            }
3072        }
3073
3074        DisplayCuesheet {
3075            cuesheet: self,
3076            filename,
3077        }
3078    }
3079
3080    /// Attempts to parse new `Cuesheet` from cue sheet file
3081    ///
3082    /// `total_samples` should be the total number
3083    /// of channel-independent samples, used to
3084    /// calculate the lead-out track
3085    ///
3086    /// `cuesheet` is the entire cuesheet as a string slice
3087    ///
3088    /// This is a simplistic cuesheet parser sufficient
3089    /// for generating FLAC-compatible CUESHEET metadata blocks.
3090    ///
3091    /// # Example File
3092    ///
3093    /// ```text
3094    /// FILE "cdimage.wav" WAVE
3095    ///   TRACK 01 AUDIO
3096    ///     INDEX 01 00:00:00
3097    ///   TRACK 02 AUDIO
3098    ///     INDEX 00 02:57:52
3099    ///     INDEX 01 03:00:02
3100    ///   TRACK 03 AUDIO
3101    ///     INDEX 00 04:46:17
3102    ///     INDEX 01 04:48:64
3103    ///   TRACK 04 AUDIO
3104    ///     INDEX 00 07:09:01
3105    ///     INDEX 01 07:11:49
3106    ///   TRACK 05 AUDIO
3107    ///     INDEX 00 09:11:47
3108    ///     INDEX 01 09:13:54
3109    ///   TRACK 06 AUDIO
3110    ///     INDEX 00 11:10:13
3111    ///     INDEX 01 11:12:51
3112    ///   TRACK 07 AUDIO
3113    ///     INDEX 00 13:03:74
3114    ///     INDEX 01 13:07:19
3115    /// ```
3116    ///
3117    /// `INDEX` points are in the format:
3118    ///
3119    /// ```text
3120    /// minutes      frames
3121    ///      ↓↓      ↓↓
3122    ///      MM::SS::FF
3123    ///          ↑↑
3124    ///     seconds
3125    /// ```
3126    ///
3127    /// There are 75 frames per second, and 60 seconds per minute.
3128    /// Since CD audio has 44100 channel-independent samples per second,
3129    /// the number of channel-independent samples per frame is 588
3130    /// (44100 ÷ 75 = 588).
3131    ///
3132    /// Thus, the sample offset of each `INDEX` point can be calculated like:
3133    ///
3134    /// samples = ((MM × 60 × 75) + (SS × 75) + FF) × 588
3135    ///
3136    /// Note that the `INDEX` points are stored in increasing order,
3137    /// as a standard single file cue sheet.
3138    ///
3139    /// # Example
3140    ///
3141    /// ```
3142    /// use flac_codec::metadata::{Cuesheet, cuesheet::{Track, Index, ISRC}};
3143    ///
3144    /// let file = "FILE \"cdimage.wav\" WAVE
3145    ///   TRACK 01 AUDIO
3146    ///     INDEX 01 00:00:00
3147    ///   TRACK 02 AUDIO
3148    ///     INDEX 00 02:57:52
3149    ///     INDEX 01 03:00:02
3150    ///   TRACK 03 AUDIO
3151    ///     INDEX 00 04:46:17
3152    ///     INDEX 01 04:48:64
3153    ///   TRACK 04 AUDIO
3154    ///     INDEX 00 07:09:01
3155    ///     INDEX 01 07:11:49
3156    ///   TRACK 05 AUDIO
3157    ///     INDEX 00 09:11:47
3158    ///     INDEX 01 09:13:54
3159    ///   TRACK 06 AUDIO
3160    ///     INDEX 00 11:10:13
3161    ///     INDEX 01 11:12:51
3162    ///   TRACK 07 AUDIO
3163    ///     INDEX 00 13:03:74
3164    ///     INDEX 01 13:07:19
3165    /// ";
3166    ///
3167    /// let cuesheet = Cuesheet::parse(39731748, file).unwrap();
3168    /// assert!(cuesheet.is_cdda());
3169    ///
3170    /// let mut tracks = cuesheet.tracks();
3171    ///
3172    /// assert_eq!(
3173    ///     tracks.next(),
3174    ///     Some(Track {
3175    ///         offset: 0,
3176    ///         number: Some(01),
3177    ///         isrc: ISRC::None,
3178    ///         non_audio: false,
3179    ///         pre_emphasis: false,
3180    ///         index_points: vec![
3181    ///             Index { number: 01, offset: 0 },
3182    ///         ],
3183    ///     }),
3184    /// );
3185    ///
3186    /// assert_eq!(
3187    ///     tracks.next(),
3188    ///     Some(Track {
3189    ///         // track's offset is that of its first index point
3190    ///         offset: ((2 * 60 * 75) + (57 * 75) + 52) * 588,
3191    ///         number: Some(02),
3192    ///         isrc: ISRC::None,
3193    ///         non_audio: false,
3194    ///         pre_emphasis: false,
3195    ///         index_points: vec![
3196    ///             // index point offsets are stored relative
3197    ///             // to the track's offset
3198    ///             Index { number: 00, offset: 0 },
3199    ///             Index { number: 01, offset: 175 * 588 }
3200    ///         ],
3201    ///     }),
3202    /// );
3203    ///
3204    /// assert_eq!(
3205    ///     tracks.next(),
3206    ///     Some(Track {
3207    ///         offset: ((4 * 60 * 75) + (46 * 75) + 17) * 588,
3208    ///         number: Some(03),
3209    ///         isrc: ISRC::None,
3210    ///         non_audio: false,
3211    ///         pre_emphasis: false,
3212    ///         index_points: vec![
3213    ///             Index { number: 00, offset: 0 },
3214    ///             Index { number: 01, offset: 197 * 588 }
3215    ///         ],
3216    ///     }),
3217    /// );
3218    ///
3219    /// // skip over some tracks for brevity
3220    /// assert_eq!(tracks.next().and_then(|track| track.number), Some(04));
3221    /// assert_eq!(tracks.next().and_then(|track| track.number), Some(05));
3222    /// assert_eq!(tracks.next().and_then(|track| track.number), Some(06));
3223    /// assert_eq!(tracks.next().and_then(|track| track.number), Some(07));
3224    ///
3225    /// // the final lead-out track has an offset of the stream's total samples
3226    /// // and no index points
3227    /// assert_eq!(
3228    ///     tracks.next(),
3229    ///     Some(Track {
3230    ///         offset: 39731748,
3231    ///         number: None,
3232    ///         isrc: ISRC::None,
3233    ///         non_audio: false,
3234    ///         pre_emphasis: false,
3235    ///         index_points: vec![],
3236    ///     }),
3237    /// );
3238    ///
3239    /// assert!(tracks.next().is_none());
3240    /// ```
3241    pub fn parse(total_samples: u64, cuesheet: &str) -> Result<Self, CuesheetError> {
3242        use cuesheet::Digit;
3243
3244        fn cdda_catalog(s: &str) -> Result<Option<[Digit; 13]>, CuesheetError> {
3245            s.chars()
3246                .map(Digit::try_from)
3247                .collect::<Result<Vec<_>, _>>()
3248                .and_then(|v| {
3249                    <[Digit; 13] as TryFrom<Vec<Digit>>>::try_from(v)
3250                        .map_err(|_| CuesheetError::InvalidCatalogNumber)
3251                })
3252                .map(Some)
3253        }
3254
3255        fn non_cdda_catalog(s: &str) -> Result<Vec<Digit>, CuesheetError> {
3256            s.chars()
3257                .map(Digit::try_from)
3258                .collect::<Result<Vec<_>, _>>()
3259                .and_then(|v| {
3260                    (v.len() <= Cuesheet::CATALOG_LEN)
3261                        .then_some(v)
3262                        .ok_or(CuesheetError::InvalidCatalogNumber)
3263                })
3264        }
3265
3266        match total_samples.try_into() {
3267            // if total samples is divisible by 588,
3268            // try to parse a CDDA cuesheet
3269            Ok(lead_out_offset) => ParsedCuesheet::parse(cuesheet, cdda_catalog).and_then(
3270                |ParsedCuesheet {
3271                     catalog_number,
3272                     tracks,
3273                 }| {
3274                    Ok(Self::CDDA {
3275                        catalog_number,
3276                        lead_in_samples: Self::LEAD_IN,
3277                        lead_out: cuesheet::LeadOutCDDA::new(tracks.last(), lead_out_offset)?,
3278                        tracks,
3279                    })
3280                },
3281            ),
3282            // if total samples isn't divisible by 588,
3283            // only try a non-CDDA cuesheet
3284            Err(_) => ParsedCuesheet::parse(cuesheet, non_cdda_catalog).and_then(
3285                |ParsedCuesheet {
3286                     catalog_number,
3287                     tracks,
3288                 }| {
3289                    Ok(Self::NonCDDA {
3290                        catalog_number,
3291                        lead_out: cuesheet::LeadOutNonCDDA::new(tracks.last(), total_samples)?,
3292                        tracks,
3293                    })
3294                },
3295            ),
3296        }
3297    }
3298
3299    fn track_offsets(&self) -> Box<dyn Iterator<Item = u64> + '_> {
3300        match self {
3301            Self::CDDA {
3302                tracks, lead_out, ..
3303            } => Box::new(
3304                tracks
3305                    .iter()
3306                    .map(|t| u64::from(t.offset + *t.index_points.start()))
3307                    .chain(std::iter::once(u64::from(lead_out.offset))),
3308            ),
3309            Self::NonCDDA {
3310                tracks, lead_out, ..
3311            } => Box::new(
3312                tracks
3313                    .iter()
3314                    .map(|t| t.offset + t.index_points.start())
3315                    .chain(std::iter::once(lead_out.offset)),
3316            ),
3317        }
3318    }
3319
3320    /// Iterates over track ranges in channel-indepedent samples
3321    ///
3322    /// Note that the range of each track is from the track
3323    /// start to the start of the next track, which is indicated
3324    /// by `INDEX 01`.
3325    /// It is *not* from the start of the pre-gaps (`INDEX 00`),
3326    /// which may not be present.
3327    ///
3328    /// ```
3329    /// use flac_codec::metadata::Cuesheet;
3330    ///
3331    /// let file = "FILE \"cdimage.wav\" WAVE
3332    ///   TRACK 01 AUDIO
3333    ///     INDEX 01 00:00:00
3334    ///   TRACK 02 AUDIO
3335    ///     INDEX 00 02:57:52
3336    ///     INDEX 01 03:00:02
3337    ///   TRACK 03 AUDIO
3338    ///     INDEX 00 04:46:17
3339    ///     INDEX 01 04:48:64
3340    ///   TRACK 04 AUDIO
3341    ///     INDEX 00 07:09:01
3342    ///     INDEX 01 07:11:49
3343    ///   TRACK 05 AUDIO
3344    ///     INDEX 00 09:11:47
3345    ///     INDEX 01 09:13:54
3346    ///   TRACK 06 AUDIO
3347    ///     INDEX 00 11:10:13
3348    ///     INDEX 01 11:12:51
3349    ///   TRACK 07 AUDIO
3350    ///     INDEX 00 13:03:74
3351    ///     INDEX 01 13:07:19
3352    /// ";
3353    ///
3354    /// let cuesheet = Cuesheet::parse(39731748, file).unwrap();
3355    /// let mut track_ranges = cuesheet.track_sample_ranges();
3356    ///
3357    /// // 00:00:00 to 03:00:02
3358    /// assert_eq!(
3359    ///     track_ranges.next(),
3360    ///     Some(0..((3 * 60 * 75) + (0 * 75) + 2) * 588)
3361    /// );
3362    ///
3363    /// // 03:00:02 to 04:48:64
3364    /// assert_eq!(
3365    ///     track_ranges.next(),
3366    ///     Some(((3 * 60 * 75) + (0 * 75) + 2) * 588..((4 * 60 * 75) + (48 * 75) + 64) * 588),
3367    /// );
3368    ///
3369    /// // skip a few tracks for brevity
3370    /// assert!(track_ranges.next().is_some()); // to 07:11.49
3371    /// assert!(track_ranges.next().is_some()); // to 09:13:54
3372    /// assert!(track_ranges.next().is_some()); // to 11:12:51
3373    /// assert!(track_ranges.next().is_some()); // to 13:07:19
3374    ///
3375    /// // 13:07:19 to the lead-out
3376    /// assert_eq!(
3377    ///     track_ranges.next(),
3378    ///     Some(((13 * 60 * 75) + (7 * 75) + 19) * 588..39731748),
3379    /// );
3380    ///
3381    /// assert!(track_ranges.next().is_none());
3382    /// ```
3383    pub fn track_sample_ranges(&self) -> impl Iterator<Item = std::ops::Range<u64>> {
3384        self.track_offsets()
3385            .zip(self.track_offsets().skip(1))
3386            .map(|(s, e)| s..e)
3387    }
3388
3389    /// Iterates over track ranges in bytes
3390    ///
3391    /// Much like [`Cuesheet::track_sample_ranges`], but takes
3392    /// a channel count and bits-per-sample to convert the ranges to bytes.
3393    ///
3394    /// For CD-DA, those values are 2 and 16, respectively.
3395    ///
3396    /// # Panics
3397    ///
3398    /// Panics if either `channel_count` or `bits_per_sample` are 0
3399    pub fn track_byte_ranges(
3400        &self,
3401        channel_count: u8,
3402        bits_per_sample: u32,
3403    ) -> impl Iterator<Item = std::ops::Range<u64>> {
3404        assert!(channel_count > 0, "channel_count must be > 0");
3405        assert!(bits_per_sample > 0, "bits_per_sample > 0");
3406
3407        let multiplier = u64::from(channel_count) * u64::from(bits_per_sample.div_ceil(8));
3408
3409        self.track_sample_ranges()
3410            .map(move |std::ops::Range { start, end }| start * multiplier..end * multiplier)
3411    }
3412}
3413
3414block!(Cuesheet, Cuesheet, true);
3415optional_block!(Cuesheet, Cuesheet);
3416
3417impl FromBitStream for Cuesheet {
3418    type Error = Error;
3419
3420    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
3421        let catalog_number: [u8; Self::CATALOG_LEN] = r.read_to()?;
3422        let lead_in_samples: u64 = r.read_to()?;
3423        let is_cdda = r.read_bit()?;
3424        r.skip(7 + 258 * 8)?;
3425        let track_count: u8 = r.read_to()?;
3426
3427        Ok(if is_cdda {
3428            Self::CDDA {
3429                catalog_number: {
3430                    match trim_nulls(&catalog_number) {
3431                        [] => None,
3432                        number => Some(
3433                            number
3434                                .iter()
3435                                .copied()
3436                                .map(cuesheet::Digit::try_from)
3437                                .collect::<Result<Vec<_>, u8>>()
3438                                // any of the digits aren't valid ASCII
3439                                .map_err(|_| Error::from(CuesheetError::InvalidCatalogNumber))?
3440                                .try_into()
3441                                // the number isn't the correct size
3442                                .map_err(|_| Error::from(CuesheetError::InvalidCatalogNumber))?,
3443                        ),
3444                    }
3445                },
3446                lead_in_samples,
3447                tracks: contiguous::Contiguous::try_collect(
3448                    (0..track_count
3449                        .checked_sub(1)
3450                        .filter(|c| *c <= 99)
3451                        .ok_or(Error::from(CuesheetError::NoTracks))?)
3452                        .map(|_| r.parse()),
3453                )
3454                .map_err(|_| Error::from(CuesheetError::TracksOutOfSequence))??,
3455                lead_out: r.parse()?,
3456            }
3457        } else {
3458            Self::NonCDDA {
3459                catalog_number: trim_nulls(&catalog_number)
3460                    .iter()
3461                    .copied()
3462                    .map(cuesheet::Digit::try_from)
3463                    .collect::<Result<Vec<_>, _>>()
3464                    .map_err(|_| Error::from(CuesheetError::InvalidCatalogNumber))?,
3465                tracks: contiguous::Contiguous::try_collect(
3466                    (0..track_count
3467                        .checked_sub(1)
3468                        .ok_or(Error::from(CuesheetError::NoTracks))?)
3469                        .map(|_| r.parse()),
3470                )
3471                .map_err(|_| Error::from(CuesheetError::TracksOutOfSequence))??,
3472                lead_out: r.parse()?,
3473            }
3474        })
3475    }
3476}
3477
3478impl ToBitStream for Cuesheet {
3479    type Error = Error;
3480
3481    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
3482        match self {
3483            Self::CDDA {
3484                catalog_number,
3485                lead_in_samples,
3486                tracks,
3487                lead_out,
3488            } => {
3489                w.write_from(match catalog_number {
3490                    Some(number) => {
3491                        let mut catalog_number = [0; Self::CATALOG_LEN];
3492                        catalog_number
3493                            .iter_mut()
3494                            .zip(number)
3495                            .for_each(|(o, i)| *o = (*i).into());
3496                        catalog_number
3497                    }
3498                    None => [0; Self::CATALOG_LEN],
3499                })?;
3500                w.write_from(*lead_in_samples)?;
3501                w.write_bit(true)?; // is CD-DA
3502                w.pad(7 + 258 * 8)?;
3503                w.write::<8, _>(u8::try_from(tracks.len() + 1).unwrap())?;
3504                for track in tracks.iter() {
3505                    w.build(track)?;
3506                }
3507                w.build(lead_out)
3508            }
3509            Self::NonCDDA {
3510                catalog_number,
3511                tracks,
3512                lead_out,
3513            } => {
3514                w.write_from({
3515                    let mut number = [0; Self::CATALOG_LEN];
3516                    number
3517                        .iter_mut()
3518                        .zip(catalog_number)
3519                        .for_each(|(o, i)| *o = (*i).into());
3520                    number
3521                })?;
3522                w.write_from::<u64>(0)?; // non-CDDA cuesheets have no lead-in samples
3523                w.write_bit(false)?; // not CD-DA
3524                w.pad(7 + 258 * 8)?;
3525                w.write::<8, _>(u8::try_from(tracks.len() + 1).unwrap())?;
3526                for track in tracks.iter() {
3527                    w.build(track)?;
3528                }
3529                w.build(lead_out)
3530            }
3531        }
3532    }
3533}
3534
3535// trims any trailing null bytes
3536fn trim_nulls(mut s: &[u8]) -> &[u8] {
3537    while let [rest @ .., 0] = s {
3538        s = rest;
3539    }
3540    s
3541}
3542
3543type ParsedCuesheetTrack<const INDEX_MAX: usize, O> =
3544    cuesheet::Track<O, NonZero<u8>, cuesheet::IndexVec<INDEX_MAX, O>>;
3545
3546struct ParsedCuesheet<const TRACK_MAX: usize, const INDEX_MAX: usize, C, O: contiguous::Adjacent> {
3547    catalog_number: C,
3548    tracks: contiguous::Contiguous<TRACK_MAX, ParsedCuesheetTrack<INDEX_MAX, O>>,
3549}
3550
3551impl<const TRACK_MAX: usize, const INDEX_MAX: usize, C, O>
3552    ParsedCuesheet<TRACK_MAX, INDEX_MAX, C, O>
3553where
3554    C: Default,
3555    O: contiguous::Adjacent
3556        + std::str::FromStr
3557        + Into<u64>
3558        + std::ops::Sub<Output = O>
3559        + Default
3560        + Copy,
3561{
3562    fn parse(
3563        cuesheet: &str,
3564        parse_catalog: impl Fn(&str) -> Result<C, CuesheetError>,
3565    ) -> Result<Self, CuesheetError> {
3566        type WipTrack<const INDEX_MAX: usize, O> = cuesheet::Track<
3567            Option<O>,
3568            NonZero<u8>,
3569            contiguous::Contiguous<INDEX_MAX, cuesheet::Index<O>>,
3570        >;
3571
3572        impl<const INDEX_MAX: usize, O: contiguous::Adjacent> WipTrack<INDEX_MAX, O> {
3573            fn new(number: NonZero<u8>) -> Self {
3574                Self {
3575                    offset: None,
3576                    number,
3577                    isrc: cuesheet::ISRC::None,
3578                    non_audio: false,
3579                    pre_emphasis: false,
3580                    index_points: contiguous::Contiguous::default(),
3581                }
3582            }
3583        }
3584
3585        impl<const INDEX_MAX: usize, O: contiguous::Adjacent> TryFrom<WipTrack<INDEX_MAX, O>>
3586            for ParsedCuesheetTrack<INDEX_MAX, O>
3587        {
3588            type Error = CuesheetError;
3589
3590            fn try_from(track: WipTrack<INDEX_MAX, O>) -> Result<Self, Self::Error> {
3591                // completed tracks need an offset which
3592                // is set by adding the first index point
3593                Ok(Self {
3594                    offset: track.offset.ok_or(CuesheetError::InvalidTrack)?,
3595                    number: track.number,
3596                    isrc: track.isrc,
3597                    non_audio: track.non_audio,
3598                    pre_emphasis: track.pre_emphasis,
3599                    index_points: track.index_points.try_into()?,
3600                })
3601            }
3602        }
3603
3604        // a bit of a hack, but should be good enough for now
3605        fn unquote(s: &str) -> &str {
3606            if s.len() > 1 && s.starts_with('"') && s.ends_with('"') {
3607                &s[1..s.len() - 1]
3608            } else {
3609                s
3610            }
3611        }
3612
3613        let mut wip_track: Option<WipTrack<INDEX_MAX, O>> = None;
3614
3615        let mut parsed = ParsedCuesheet {
3616            catalog_number: None,
3617            tracks: contiguous::Contiguous::default(),
3618        };
3619
3620        for line in cuesheet.lines() {
3621            let line = line.trim();
3622            match line.split_once(' ').unwrap_or((line, "")) {
3623                ("CATALOG", "") => return Err(CuesheetError::CatalogMissingNumber),
3624                ("CATALOG", number) => match parsed.catalog_number {
3625                    Some(_) => return Err(CuesheetError::MultipleCatalogNumber),
3626                    ref mut num @ None => {
3627                        *num = Some(parse_catalog(unquote(number))?);
3628                    }
3629                },
3630                ("TRACK", rest) => {
3631                    if let Some(finished) = wip_track.replace(WipTrack::new(
3632                        rest.split_once(' ')
3633                            .ok_or(CuesheetError::InvalidTrack)?
3634                            .0
3635                            .parse()
3636                            .map_err(|_| CuesheetError::InvalidTrack)?,
3637                    )) {
3638                        parsed
3639                            .tracks
3640                            .try_push(finished.try_into()?)
3641                            .map_err(|_| CuesheetError::TracksOutOfSequence)?
3642                    }
3643                }
3644                ("INDEX", rest) => {
3645                    let (number, offset) = rest
3646                        .split_once(' ')
3647                        .ok_or(CuesheetError::InvalidIndexPoint)?;
3648
3649                    let number: u8 = number
3650                        .parse()
3651                        .map_err(|_| CuesheetError::InvalidIndexPoint)?;
3652
3653                    let offset: O = offset
3654                        .parse()
3655                        .map_err(|_| CuesheetError::InvalidIndexPoint)?;
3656
3657                    let wip_track = wip_track.as_mut().ok_or(CuesheetError::PrematureIndex)?;
3658
3659                    let index = match &mut wip_track.offset {
3660                        // work-in progress track has no offset,
3661                        // so we set it from this index point's
3662                        // and set the index point's offset to 0
3663                        track_offset @ None => {
3664                            // the first index of the first track must have an offset of 0
3665                            if parsed.tracks.is_empty() && offset.into() != 0 {
3666                                return Err(CuesheetError::NonZeroFirstIndex);
3667                            }
3668
3669                            *track_offset = Some(offset);
3670
3671                            cuesheet::Index {
3672                                number,
3673                                offset: O::default(),
3674                            }
3675                        }
3676                        Some(track_offset) => {
3677                            // work-in-progress track has offset,
3678                            // so deduct that offset from this index point's
3679
3680                            cuesheet::Index {
3681                                number,
3682                                offset: offset - *track_offset,
3683                            }
3684                        }
3685                    };
3686
3687                    wip_track
3688                        .index_points
3689                        .try_push(index)
3690                        .map_err(|_| CuesheetError::IndexPointsOutOfSequence)?;
3691                }
3692                ("ISRC", isrc) => {
3693                    use cuesheet::ISRC;
3694
3695                    let wip_track = wip_track.as_mut().ok_or(CuesheetError::PrematureISRC)?;
3696
3697                    if !wip_track.index_points.is_empty() {
3698                        return Err(CuesheetError::LateISRC)?;
3699                    }
3700
3701                    match &mut wip_track.isrc {
3702                        track_isrc @ ISRC::None => {
3703                            *track_isrc = ISRC::String(
3704                                unquote(isrc)
3705                                    .parse()
3706                                    .map_err(|_| CuesheetError::InvalidISRC)?,
3707                            );
3708                        }
3709                        ISRC::String(_) => return Err(CuesheetError::MultipleISRC),
3710                    }
3711                }
3712                ("FLAGS", "PRE") => {
3713                    let wip_track = wip_track.as_mut().ok_or(CuesheetError::PrematureFlags)?;
3714
3715                    if !wip_track.index_points.is_empty() {
3716                        return Err(CuesheetError::LateFlags)?;
3717                    } else {
3718                        wip_track.pre_emphasis = true;
3719                    }
3720                }
3721                _ => { /*do nothing for now*/ }
3722            }
3723        }
3724
3725        parsed
3726            .tracks
3727            .try_push(
3728                wip_track
3729                    .take()
3730                    .ok_or(CuesheetError::NoTracks)?
3731                    .try_into()?,
3732            )
3733            .map_err(|_| CuesheetError::TracksOutOfSequence)?;
3734
3735        Ok(ParsedCuesheet {
3736            catalog_number: parsed.catalog_number.unwrap_or_default(),
3737            tracks: parsed.tracks,
3738        })
3739    }
3740}
3741
3742/// An error when trying to parse cue sheet data
3743#[derive(Debug)]
3744#[non_exhaustive]
3745pub enum CuesheetError {
3746    /// CATALOG tag missing catalog number
3747    CatalogMissingNumber,
3748    /// multiple CATALOG numbers found
3749    MultipleCatalogNumber,
3750    /// invalid CATALOG number
3751    InvalidCatalogNumber,
3752    /// Multiple ISRC numbers
3753    MultipleISRC,
3754    /// Invalid ISRC number
3755    InvalidISRC,
3756    /// ISRC number found before TRACK
3757    PrematureISRC,
3758    /// ISRC number after INDEX points
3759    LateISRC,
3760    /// FLAGS seen before TRACK
3761    PrematureFlags,
3762    /// FLAGS seen after INDEX points
3763    LateFlags,
3764    /// ISRC tag missing number
3765    ISRCMissingNumber,
3766    /// Unable to parse TRACK field correctly
3767    InvalidTrack,
3768    /// No tracks in cue sheet
3769    NoTracks,
3770    /// Non-Zero starting INDEX in first TRACK
3771    NonZeroStartingIndex,
3772    /// INDEX point occurs before TRACK
3773    PrematureIndex,
3774    /// Invalid INDEX point in cuesheet
3775    InvalidIndexPoint,
3776    /// No INDEX Points in track
3777    NoIndexPoints,
3778    /// Excessive tracks in cue sheet
3779    ExcessiveTracks,
3780    /// INDEX points in track are out of sequence
3781    IndexPointsOutOfSequence,
3782    /// TRACK points in CUESHEET are out of sequence
3783    TracksOutOfSequence,
3784    /// Lead-out track is not beyond all track indices
3785    ShortLeadOut,
3786    /// INDEX points in lead-out TRACK
3787    IndexPointsInLeadout,
3788    /// Invalid offset for CD-DA CUESHEET
3789    InvalidCDDAOffset,
3790    /// first INDEX of first TRACK doesn't have offset of 0
3791    NonZeroFirstIndex,
3792}
3793
3794impl std::error::Error for CuesheetError {}
3795
3796impl std::fmt::Display for CuesheetError {
3797    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3798        match self {
3799            Self::CatalogMissingNumber => "CATALOG tag missing number".fmt(f),
3800            Self::MultipleCatalogNumber => "multiple CATALOG numbers found".fmt(f),
3801            Self::InvalidCatalogNumber => "invalid CATALOG number".fmt(f),
3802            Self::MultipleISRC => "multiple ISRC numbers found for track".fmt(f),
3803            Self::InvalidISRC => "invalid ISRC number found".fmt(f),
3804            Self::PrematureISRC => "ISRC number found before TRACK".fmt(f),
3805            Self::LateISRC => "ISRC number found after INDEX points".fmt(f),
3806            Self::PrematureFlags => "FLAGS found before TRACK".fmt(f),
3807            Self::LateFlags => "FLAGS found after INDEX points".fmt(f),
3808            Self::ISRCMissingNumber => "ISRC tag missing number".fmt(f),
3809            Self::InvalidTrack => "invalid TRACK entry".fmt(f),
3810            Self::NoTracks => "no TRACK entries in cue sheet".fmt(f),
3811            Self::NonZeroStartingIndex => {
3812                "first INDEX of first track must have 00:00:00 offset".fmt(f)
3813            }
3814            Self::PrematureIndex => "INDEX found before TRACK".fmt(f),
3815            Self::InvalidIndexPoint => "invalid INDEX entry".fmt(f),
3816            Self::NoIndexPoints => "no INDEX points in track".fmt(f),
3817            Self::ExcessiveTracks => "excessive tracks in CUESHEET".fmt(f),
3818            Self::IndexPointsOutOfSequence => "INDEX points out of sequence".fmt(f),
3819            Self::TracksOutOfSequence => "TRACKS out of sequence".fmt(f),
3820            Self::ShortLeadOut => "lead-out track not beyond final INDEX point".fmt(f),
3821            Self::IndexPointsInLeadout => "INDEX points in lead-out TRACK".fmt(f),
3822            Self::InvalidCDDAOffset => "invalid offset for CD-DA CUESHEET".fmt(f),
3823            Self::NonZeroFirstIndex => "first index of first track has non-zero offset".fmt(f),
3824        }
3825    }
3826}
3827
3828/// A PICTURE metadata block
3829///
3830/// Picture blocks are for embedding artwork
3831/// such as album covers, liner notes, etc.
3832///
3833/// This block may occur multiple times in a FLAC file.
3834///
3835/// | Bits | Field | Meaning |
3836/// |-----:|------:|---------|
3837/// | 32   | `picture_type` | picture type
3838/// | 32   | media type len | media type length, in bytes
3839/// | `media type len`×8 | `media_type` | picture's MIME type
3840/// | 32   | description len | description length, in bytes
3841/// | `description len`×8 | `description` | description of picture, in UTF-8
3842/// | 32   | `width` | width of picture, in pixels
3843/// | 32   | `height`| height of picture, in pixels
3844/// | 32   | `color_depth` | color depth of picture in bits-per-pixel
3845/// | 32   | `colors_used` | for indexed-color pictures, number of colors used
3846/// | 32   | data len | length of picture data, in bytes
3847/// | `data len`×8 | `data` | raw picture data
3848///
3849/// # Example
3850/// ```
3851/// use bitstream_io::{BitReader, BitRead, BigEndian};
3852/// use flac_codec::metadata::{Picture, PictureType};
3853///
3854/// let data: &[u8] = &[
3855///     0x00, 0x00, 0x00, 0x03,  // picture type
3856///     0x00, 0x00, 0x00, 0x09,  // media type len (9 bytes)
3857///     0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67,
3858///     0x00, 0x00, 0x00, 0x0a,  // description len (10 bytes)
3859///     0x54, 0x65, 0x73, 0x74, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65,
3860///     0x00, 0x00, 0x00, 0x10,  // width
3861///     0x00, 0x00, 0x00, 0x09,  // height
3862///     0x00, 0x00, 0x00, 0x18,  // color depth
3863///     0x00, 0x00, 0x00, 0x00,  // color count
3864///     0x00, 0x00, 0x00, 0x5c,  // data len (92 bytes)
3865///     0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
3866///     0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
3867///     0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x09,
3868///     0x08, 0x02, 0x00, 0x00, 0x00, 0xb4, 0x48, 0x3b,
3869///     0x65, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
3870///     0x73, 0x00, 0x00, 0x2e, 0x23, 0x00, 0x00, 0x2e,
3871///     0x23, 0x01, 0x78, 0xa5, 0x3f, 0x76, 0x00, 0x00,
3872///     0x00, 0x0e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3,
3873///     0x63, 0x60, 0x18, 0x05, 0x43, 0x12, 0x00, 0x00,
3874///     0x01, 0xb9, 0x00, 0x01, 0xed, 0x78, 0x29, 0x25,
3875///     0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
3876///     0xae, 0x42, 0x60, 0x82,
3877/// ];
3878///
3879/// let mut r = BitReader::endian(data, BigEndian);
3880/// assert_eq!(
3881///     r.parse::<Picture>().unwrap(),
3882///     Picture {
3883///         picture_type: PictureType::FrontCover,  // type 3
3884///         media_type: "image/png".to_owned(),
3885///         description: "Test Image".to_owned(),
3886///         width: 0x00_00_00_10,                   // 16 pixels
3887///         height: 0x00_00_00_09,                  // 9 pixels
3888///         color_depth: 0x00_00_00_18,             // 24 bits-per-pixel
3889///         colors_used: None,                      // not indexed
3890///         data: vec![
3891///             0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
3892///             0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
3893///             0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x09,
3894///             0x08, 0x02, 0x00, 0x00, 0x00, 0xb4, 0x48, 0x3b,
3895///             0x65, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
3896///             0x73, 0x00, 0x00, 0x2e, 0x23, 0x00, 0x00, 0x2e,
3897///             0x23, 0x01, 0x78, 0xa5, 0x3f, 0x76, 0x00, 0x00,
3898///             0x00, 0x0e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3,
3899///             0x63, 0x60, 0x18, 0x05, 0x43, 0x12, 0x00, 0x00,
3900///             0x01, 0xb9, 0x00, 0x01, 0xed, 0x78, 0x29, 0x25,
3901///             0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
3902///             0xae, 0x42, 0x60, 0x82,
3903///         ],
3904///     },
3905/// );
3906/// ```
3907#[derive(Debug, Clone, Eq, PartialEq)]
3908pub struct Picture {
3909    /// The picture type
3910    pub picture_type: PictureType,
3911    /// The media type string as specified by RFC2046
3912    pub media_type: String,
3913    /// The description of the picture
3914    pub description: String,
3915    /// The width of the picture in pixels
3916    pub width: u32,
3917    /// The height of the picture in pixels
3918    pub height: u32,
3919    /// The color depth of the picture in bits per pixel
3920    pub color_depth: u32,
3921    /// For indexed-color pictures, the number of colors used
3922    pub colors_used: Option<NonZero<u32>>,
3923    /// The binary picture data
3924    pub data: Vec<u8>,
3925}
3926
3927block!(Picture, Picture, true);
3928optional_block!(Picture, Picture);
3929
3930impl Picture {
3931    /// Attempt to create a new PICTURE block from raw image data
3932    ///
3933    /// Currently supported image types for this method are:
3934    ///
3935    /// - JPEG
3936    /// - PNG
3937    /// - GIF
3938    ///
3939    /// Any type of image data may be placed in a PICTURE block,
3940    /// but the user may have to use external crates
3941    /// to determine their proper image metrics
3942    /// to build a block from.
3943    ///
3944    /// # Errors
3945    ///
3946    /// Returns an error if some problem occurs reading
3947    /// or identifying the file.
3948    pub fn new<S, V>(
3949        picture_type: PictureType,
3950        description: S,
3951        data: V,
3952    ) -> Result<Self, InvalidPicture>
3953    where
3954        S: Into<String>,
3955        V: Into<Vec<u8>> + AsRef<[u8]>,
3956    {
3957        let metrics = PictureMetrics::try_new(data.as_ref())?;
3958        Ok(Self {
3959            picture_type,
3960            description: description.into(),
3961            data: data.into(),
3962            media_type: metrics.media_type.to_owned(),
3963            width: metrics.width,
3964            height: metrics.height,
3965            color_depth: metrics.color_depth,
3966            colors_used: metrics.colors_used,
3967        })
3968    }
3969
3970    /// Attempt to create new PICTURE block from file on disk
3971    pub fn open<S, P>(
3972        picture_type: PictureType,
3973        description: S,
3974        path: P,
3975    ) -> Result<Self, InvalidPicture>
3976    where
3977        S: Into<String>,
3978        P: AsRef<Path>,
3979    {
3980        std::fs::read(path)
3981            .map_err(InvalidPicture::Io)
3982            .and_then(|data| Self::new(picture_type, description, data))
3983    }
3984}
3985
3986impl FromBitStream for Picture {
3987    type Error = Error;
3988
3989    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
3990        fn prefixed_field<R: BitRead + ?Sized>(r: &mut R) -> std::io::Result<Vec<u8>> {
3991            let size = r.read_to::<u32>()?;
3992            r.read_to_vec(size.try_into().unwrap())
3993        }
3994
3995        Ok(Self {
3996            picture_type: r.parse()?,
3997            media_type: String::from_utf8(prefixed_field(r)?)?,
3998            description: String::from_utf8(prefixed_field(r)?)?,
3999            width: r.read_to()?,
4000            height: r.read_to()?,
4001            color_depth: r.read_to()?,
4002            colors_used: r.read::<32, _>()?,
4003            data: prefixed_field(r)?,
4004        })
4005    }
4006}
4007
4008impl ToBitStream for Picture {
4009    type Error = Error;
4010
4011    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Error> {
4012        fn prefixed_field<W: BitWrite + ?Sized>(
4013            w: &mut W,
4014            field: &[u8],
4015            error: Error,
4016        ) -> Result<(), Error> {
4017            w.write_from::<u32>(field.len().try_into().map_err(|_| error)?)
4018                .map_err(Error::Io)?;
4019            w.write_bytes(field).map_err(Error::Io)
4020        }
4021
4022        w.build(&self.picture_type)?;
4023        prefixed_field(w, self.media_type.as_bytes(), Error::ExcessiveStringLength)?;
4024        prefixed_field(w, self.description.as_bytes(), Error::ExcessiveStringLength)?;
4025        w.write_from(self.width)?;
4026        w.write_from(self.height)?;
4027        w.write_from(self.color_depth)?;
4028        w.write::<32, _>(self.colors_used)?;
4029        prefixed_field(w, &self.data, Error::ExcessivePictureSize)
4030    }
4031}
4032
4033/// Defined variants of PICTURE type
4034#[derive(Debug, Copy, Clone, Eq, PartialEq)]
4035pub enum PictureType {
4036    /// Other
4037    Other = 0,
4038    /// PNG file icon of 32x32 pixels
4039    Png32x32 = 1,
4040    /// General file icon
4041    GeneralFileIcon = 2,
4042    /// Front cover
4043    FrontCover = 3,
4044    /// Back cover
4045    BackCover = 4,
4046    /// Liner notes page
4047    LinerNotes = 5,
4048    /// Media label (e.g., CD, Vinyl or Cassette label)
4049    MediaLabel = 6,
4050    /// Lead artist, lead performer, or soloist
4051    LeadArtist = 7,
4052    /// Artist or performer
4053    Artist = 8,
4054    /// Conductor
4055    Conductor = 9,
4056    /// Band or orchestra
4057    Band = 10,
4058    /// Composer
4059    Composer = 11,
4060    /// Lyricist or text writer
4061    Lyricist = 12,
4062    /// Recording location
4063    RecordingLocation = 13,
4064    /// During recording
4065    DuringRecording = 14,
4066    /// During performance
4067    DuringPerformance = 15,
4068    /// Movie or video screen capture
4069    ScreenCapture = 16,
4070    /// A bright colored fish
4071    Fish = 17,
4072    /// Illustration
4073    Illustration = 18,
4074    /// Band or artist logotype
4075    BandLogo = 19,
4076    /// Publisher or studio logotype
4077    PublisherLogo = 20,
4078}
4079
4080impl std::fmt::Display for PictureType {
4081    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4082        match self {
4083            Self::Other => "Other".fmt(f),
4084            Self::Png32x32 => "32×32 PNG Icon".fmt(f),
4085            Self::GeneralFileIcon => "General File Icon".fmt(f),
4086            Self::FrontCover => "Cover (front)".fmt(f),
4087            Self::BackCover => "Cover (back)".fmt(f),
4088            Self::LinerNotes => "Liner Notes".fmt(f),
4089            Self::MediaLabel => "Media Label".fmt(f),
4090            Self::LeadArtist => "Lead Artist".fmt(f),
4091            Self::Artist => "Artist".fmt(f),
4092            Self::Conductor => "Conductor".fmt(f),
4093            Self::Band => "Band or Orchestra".fmt(f),
4094            Self::Composer => "Composer".fmt(f),
4095            Self::Lyricist => "lyricist or Text Writer".fmt(f),
4096            Self::RecordingLocation => "Recording Location".fmt(f),
4097            Self::DuringRecording => "During Recording".fmt(f),
4098            Self::DuringPerformance => "During Performance".fmt(f),
4099            Self::ScreenCapture => "Movie or Video Screen Capture".fmt(f),
4100            Self::Fish => "A Bright Colored Fish".fmt(f),
4101            Self::Illustration => "Illustration".fmt(f),
4102            Self::BandLogo => "Band or Artist Logotype".fmt(f),
4103            Self::PublisherLogo => "Publisher or Studio Logotype".fmt(f),
4104        }
4105    }
4106}
4107
4108impl FromBitStream for PictureType {
4109    type Error = Error;
4110
4111    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
4112        match r.read_to::<u32>()? {
4113            0 => Ok(Self::Other),
4114            1 => Ok(Self::Png32x32),
4115            2 => Ok(Self::GeneralFileIcon),
4116            3 => Ok(Self::FrontCover),
4117            4 => Ok(Self::BackCover),
4118            5 => Ok(Self::LinerNotes),
4119            6 => Ok(Self::MediaLabel),
4120            7 => Ok(Self::LeadArtist),
4121            8 => Ok(Self::Artist),
4122            9 => Ok(Self::Conductor),
4123            10 => Ok(Self::Band),
4124            11 => Ok(Self::Composer),
4125            12 => Ok(Self::Lyricist),
4126            13 => Ok(Self::RecordingLocation),
4127            14 => Ok(Self::DuringRecording),
4128            15 => Ok(Self::DuringPerformance),
4129            16 => Ok(Self::ScreenCapture),
4130            17 => Ok(Self::Fish),
4131            18 => Ok(Self::Illustration),
4132            19 => Ok(Self::BandLogo),
4133            20 => Ok(Self::PublisherLogo),
4134            _ => Err(Error::InvalidPictureType),
4135        }
4136    }
4137}
4138
4139impl ToBitStream for PictureType {
4140    type Error = std::io::Error;
4141
4142    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
4143        w.write_from::<u32>(match self {
4144            Self::Other => 0,
4145            Self::Png32x32 => 1,
4146            Self::GeneralFileIcon => 2,
4147            Self::FrontCover => 3,
4148            Self::BackCover => 4,
4149            Self::LinerNotes => 5,
4150            Self::MediaLabel => 6,
4151            Self::LeadArtist => 7,
4152            Self::Artist => 8,
4153            Self::Conductor => 9,
4154            Self::Band => 10,
4155            Self::Composer => 11,
4156            Self::Lyricist => 12,
4157            Self::RecordingLocation => 13,
4158            Self::DuringRecording => 14,
4159            Self::DuringPerformance => 15,
4160            Self::ScreenCapture => 16,
4161            Self::Fish => 17,
4162            Self::Illustration => 18,
4163            Self::BandLogo => 19,
4164            Self::PublisherLogo => 20,
4165        })
4166    }
4167}
4168
4169/// An error when trying to identify a picture's metrics
4170#[derive(Debug)]
4171#[non_exhaustive]
4172pub enum InvalidPicture {
4173    /// An I/O Error
4174    Io(std::io::Error),
4175    /// Unsupported Image Format
4176    Unsupported,
4177    /// Invalid PNG File
4178    Png(&'static str),
4179    /// Invalid JPEG File
4180    Jpeg(&'static str),
4181    /// Invalid GIF File
4182    Gif(&'static str),
4183}
4184
4185impl From<std::io::Error> for InvalidPicture {
4186    #[inline]
4187    fn from(err: std::io::Error) -> Self {
4188        Self::Io(err)
4189    }
4190}
4191
4192impl std::error::Error for InvalidPicture {}
4193
4194impl std::fmt::Display for InvalidPicture {
4195    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4196        match self {
4197            Self::Io(err) => err.fmt(f),
4198            Self::Unsupported => "unsupported image format".fmt(f),
4199            Self::Png(s) => write!(f, "PNG parsing error : {s}"),
4200            Self::Jpeg(s) => write!(f, "JPEG parsing error : {s}"),
4201            Self::Gif(s) => write!(f, "GIF parsing error : {s}"),
4202        }
4203    }
4204}
4205
4206struct PictureMetrics {
4207    media_type: &'static str,
4208    width: u32,
4209    height: u32,
4210    color_depth: u32,
4211    colors_used: Option<NonZero<u32>>,
4212}
4213
4214impl PictureMetrics {
4215    fn try_new(data: &[u8]) -> Result<Self, InvalidPicture> {
4216        if data.starts_with(b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
4217            Self::try_png(data)
4218        } else if data.starts_with(b"\xFF\xD8\xFF") {
4219            Self::try_jpeg(data)
4220        } else if data.starts_with(b"GIF") {
4221            Self::try_gif(data)
4222        } else {
4223            Err(InvalidPicture::Unsupported)
4224        }
4225    }
4226
4227    fn try_png(data: &[u8]) -> Result<Self, InvalidPicture> {
4228        // this is an *extremely* cut-down PNG parser
4229        // that handles just enough of the format to get
4230        // image metadata, but does *not* validate things
4231        // like the block CRC32s
4232
4233        fn plte_colors<R: ByteRead>(mut r: R) -> Result<u32, InvalidPicture> {
4234            loop {
4235                let block_len = r.read::<u32>()?;
4236                match r.read::<[u8; 4]>()?.as_slice() {
4237                    b"PLTE" => {
4238                        if block_len % 3 == 0 {
4239                            break Ok(block_len / 3);
4240                        } else {
4241                            break Err(InvalidPicture::Png("invalid PLTE length"));
4242                        }
4243                    }
4244                    _ => {
4245                        r.skip(block_len)?;
4246                        let _crc = r.read::<u32>()?;
4247                    }
4248                }
4249            }
4250        }
4251
4252        let mut r = ByteReader::endian(data, BigEndian);
4253        if &r.read::<[u8; 8]>()? != b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" {
4254            return Err(InvalidPicture::Png("not a PNG image"));
4255        }
4256
4257        // IHDR chunk must be first
4258        if r.read::<u32>()? != 0x0d {
4259            return Err(InvalidPicture::Png("invalid IHDR length"));
4260        }
4261        if &r.read::<[u8; 4]>()? != b"IHDR" {
4262            return Err(InvalidPicture::Png("IHDR chunk not first"));
4263        }
4264        let width = r.read()?;
4265        let height = r.read()?;
4266        let bit_depth = r.read::<u8>()?;
4267        let color_type = r.read::<u8>()?;
4268        let _compression_method = r.read::<u8>()?;
4269        let _filter_method = r.read::<u8>()?;
4270        let _interlace_method = r.read::<u8>()?;
4271        let _crc = r.read::<u32>()?;
4272
4273        let (color_depth, colors_used) = match color_type {
4274            0 => (bit_depth.into(), None),           // grayscale
4275            2 => ((bit_depth * 3).into(), None),     // RGB
4276            3 => (0, NonZero::new(plte_colors(r)?)), // palette
4277            4 => ((bit_depth * 2).into(), None),     // grayscale + alpha
4278            6 => ((bit_depth * 4).into(), None),     // RGB + alpha
4279            _ => return Err(InvalidPicture::Png("invalid color type")),
4280        };
4281
4282        Ok(Self {
4283            media_type: "image/png",
4284            width,
4285            height,
4286            color_depth,
4287            colors_used,
4288        })
4289    }
4290
4291    fn try_jpeg(data: &[u8]) -> Result<Self, InvalidPicture> {
4292        let mut r = ByteReader::endian(data, BigEndian);
4293
4294        if r.read::<u8>()? != 0xFF || r.read::<u8>()? != 0xD8 {
4295            return Err(InvalidPicture::Jpeg("invalid JPEG marker"));
4296        }
4297
4298        loop {
4299            if r.read::<u8>()? != 0xFF {
4300                break Err(InvalidPicture::Jpeg("invalid JPEG marker"));
4301            }
4302            match r.read::<u8>()? {
4303                0xC0 | 0xC1 | 0xC2 | 0xC3 | 0xC5 | 0xC6 | 0xC7 | 0xC9 | 0xCA | 0xCB | 0xCD
4304                | 0xCE | 0xCF => {
4305                    let _len = r.read::<u16>()?;
4306                    let data_precision = r.read::<u8>()?;
4307                    let height = r.read::<u16>()?;
4308                    let width = r.read::<u16>()?;
4309                    let components = r.read::<u8>()?;
4310                    break Ok(Self {
4311                        media_type: "image/jpeg",
4312                        width: width.into(),
4313                        height: height.into(),
4314                        color_depth: (data_precision * components).into(),
4315                        colors_used: None,
4316                    });
4317                }
4318                _ => {
4319                    let segment_length = r
4320                        .read::<u16>()?
4321                        .checked_sub(2)
4322                        .ok_or(InvalidPicture::Jpeg("invalid segment length"))?;
4323                    r.skip(segment_length.into())?;
4324                }
4325            }
4326        }
4327    }
4328
4329    fn try_gif(data: &[u8]) -> Result<Self, InvalidPicture> {
4330        let mut r = BitReader::endian(data, LittleEndian);
4331
4332        if &r.read_to::<[u8; 3]>()? != b"GIF" {
4333            return Err(InvalidPicture::Gif("invalid GIF signature"));
4334        }
4335
4336        r.skip(3 * 8)?; // ignore version bytes
4337
4338        Ok(Self {
4339            media_type: "image/gif",
4340            width: r.read::<16, _>()?,
4341            height: r.read::<16, _>()?,
4342            colors_used: NonZero::new(1 << (r.read::<3, u32>()? + 1)),
4343            color_depth: 0,
4344        })
4345    }
4346}
4347
4348/// A collection of metadata blocks
4349///
4350/// This collection enforces the restriction that FLAC files
4351/// must always contain a STREAMINFO metadata block
4352/// and that block must always be first in the file.
4353///
4354/// Because it is required, that block may be retrieved
4355/// unconditionally from this collection, while all others
4356/// are optional and may appear in any order.
4357#[derive(Clone, Debug)]
4358pub struct BlockList {
4359    streaminfo: Streaminfo,
4360    blocks: Vec<private::OptionalBlock>,
4361}
4362
4363impl BlockList {
4364    /// Creates `BlockList` from initial STREAMINFO
4365    pub fn new(streaminfo: Streaminfo) -> Self {
4366        Self {
4367            streaminfo,
4368            blocks: Vec::default(),
4369        }
4370    }
4371
4372    /// Reads `BlockList` from the given reader
4373    ///
4374    /// This assumes the reader is rewound to the
4375    /// beginning of the file.
4376    ///
4377    /// Because this may perform many small reads,
4378    /// using a buffered reader is preferred when
4379    /// reading from a raw file.
4380    ///
4381    /// # Errors
4382    ///
4383    /// Returns any error reading or parsing metadata blocks
4384    pub fn read<R: std::io::Read>(r: R) -> Result<Self, Error> {
4385        // TODO - change this to flatten once that stabilizes
4386        read_blocks(r).collect::<Result<Result<Self, _>, _>>()?
4387    }
4388
4389    /// Reads `BlockList` from the given file path
4390    ///
4391    /// # Errors
4392    ///
4393    /// Returns any error reading or parsing metadata blocks
4394    pub fn open<P: AsRef<Path>>(p: P) -> Result<Self, Error> {
4395        File::open(p.as_ref())
4396            .map(BufReader::new)
4397            .map_err(Error::Io)
4398            .and_then(BlockList::read)
4399    }
4400
4401    /// Returns reference to our STREAMINFO metadata block
4402    pub fn streaminfo(&self) -> &Streaminfo {
4403        &self.streaminfo
4404    }
4405
4406    /// Returns exclusive reference to our STREAMINFO metadata block
4407    ///
4408    /// Care must be taken when modifying the STREAMINFO, or
4409    /// one's file could be rendered unplayable.
4410    pub fn streaminfo_mut(&mut self) -> &mut Streaminfo {
4411        &mut self.streaminfo
4412    }
4413
4414    /// Iterates over all the metadata blocks
4415    pub fn blocks(&self) -> impl Iterator<Item = BlockRef<'_>> {
4416        std::iter::once(self.streaminfo.as_block_ref())
4417            .chain(self.blocks.iter().map(|b| b.as_block_ref()))
4418    }
4419
4420    /// Inserts new optional metadata block
4421    ///
4422    /// If the block may only occur once in the stream
4423    /// (such as the SEEKTABLE), any existing block of
4424    /// the same type removed and extracted first.
4425    pub fn insert<B: OptionalMetadataBlock>(&mut self, block: B) -> Option<B> {
4426        if B::MULTIPLE {
4427            self.blocks.push(block.into());
4428            None
4429        } else {
4430            match self
4431                .blocks
4432                .iter_mut()
4433                .find_map(|b| B::try_from_opt_block_mut(b).ok())
4434            {
4435                Some(b) => Some(std::mem::replace(b, block)),
4436                None => {
4437                    self.blocks.push(block.into());
4438                    None
4439                }
4440            }
4441        }
4442    }
4443
4444    /// Gets reference to metadata block, if present
4445    ///
4446    /// If the block type occurs multiple times,
4447    /// this returns the first instance.
4448    pub fn get<B: OptionalMetadataBlock>(&self) -> Option<&B> {
4449        self.blocks
4450            .iter()
4451            .find_map(|b| B::try_from_opt_block(b).ok())
4452    }
4453
4454    /// Gets mutable reference to metadata block, if present
4455    ///
4456    /// If the block type occurs multiple times,
4457    /// this returns the first instance.
4458    pub fn get_mut<B: OptionalMetadataBlock>(&mut self) -> Option<&mut B> {
4459        self.blocks
4460            .iter_mut()
4461            .find_map(|b| B::try_from_opt_block_mut(b).ok())
4462    }
4463
4464    /// Gets mutable references to a pair of metadata blocks
4465    ///
4466    /// If either block type occurs multiple times,
4467    /// this returns the first instance.
4468    pub fn get_pair_mut<B, C>(&mut self) -> (Option<&mut B>, Option<&mut C>)
4469    where
4470        B: OptionalMetadataBlock,
4471        C: OptionalMetadataBlock,
4472    {
4473        use std::ops::ControlFlow;
4474
4475        match self
4476            .blocks
4477            .iter_mut()
4478            .try_fold((None, None), |acc, block| match acc {
4479                (first @ None, second @ None) => {
4480                    ControlFlow::Continue(match B::try_from_opt_block_mut(block) {
4481                        Ok(first) => (Some(first), second),
4482                        Err(block) => (first, C::try_from_opt_block_mut(block).ok()),
4483                    })
4484                }
4485                (first @ Some(_), None) => {
4486                    ControlFlow::Continue((first, C::try_from_opt_block_mut(block).ok()))
4487                }
4488                (None, second @ Some(_)) => {
4489                    ControlFlow::Continue((B::try_from_opt_block_mut(block).ok(), second))
4490                }
4491                pair @ (Some(_), Some(_)) => ControlFlow::Break(pair),
4492            }) {
4493            ControlFlow::Break(p) | ControlFlow::Continue(p) => p,
4494        }
4495    }
4496
4497    /// Gets references to all metadata blocks of the given type
4498    pub fn get_all<'b, B: OptionalMetadataBlock + 'b>(&'b self) -> impl Iterator<Item = &'b B> {
4499        self.blocks
4500            .iter()
4501            .filter_map(|b| B::try_from_opt_block(b).ok())
4502    }
4503
4504    /// Gets exclusive references to all metadata blocks of the given type
4505    pub fn get_all_mut<'b, B: OptionalMetadataBlock + 'b>(
4506        &'b mut self,
4507    ) -> impl Iterator<Item = &'b mut B> {
4508        self.blocks
4509            .iter_mut()
4510            .filter_map(|b| B::try_from_opt_block_mut(b).ok())
4511    }
4512
4513    /// Returns `true` if block exists in list
4514    pub fn has<B: OptionalMetadataBlock>(&self) -> bool {
4515        self.get::<B>().is_some()
4516    }
4517
4518    /// Removes all instances of the given metadata block type
4519    pub fn remove<B: OptionalMetadataBlock>(&mut self) {
4520        self.blocks.retain(|b| b.block_type() != B::TYPE)
4521    }
4522
4523    /// Removes and returns all instances of the given block type
4524    pub fn extract<B: OptionalMetadataBlock>(&mut self) -> impl Iterator<Item = B> {
4525        self.blocks
4526            .extract_if(.., |block| block.block_type() == B::TYPE)
4527            .filter_map(|b| B::try_from(b).ok())
4528    }
4529
4530    /// Updates first instance of the given block, creating it if necessary
4531    ///
4532    /// # Example
4533    ///
4534    /// ```
4535    /// use flac_codec::metadata::{BlockList, Streaminfo, VorbisComment};
4536    /// use flac_codec::metadata::fields::ARTIST;
4537    ///
4538    /// // build a BlockList with a dummy Streaminfo
4539    /// let mut blocklist = BlockList::new(
4540    ///     Streaminfo {
4541    ///         minimum_block_size: 0,
4542    ///         maximum_block_size: 0,
4543    ///         minimum_frame_size: None,
4544    ///         maximum_frame_size: None,
4545    ///         sample_rate: 44100,
4546    ///         channels: 1u8.try_into().unwrap(),
4547    ///         bits_per_sample: 16u32.try_into().unwrap(),
4548    ///         total_samples: None,
4549    ///         md5: None,
4550    ///     },
4551    /// );
4552    ///
4553    /// // the block starts out with no comment
4554    /// assert!(blocklist.get::<VorbisComment>().is_none());
4555    ///
4556    /// // update Vorbis Comment with artist field,
4557    /// // which adds a new block to the list
4558    /// blocklist.update::<VorbisComment>(
4559    ///     |vc| vc.insert(ARTIST, "Artist 1")
4560    /// );
4561    /// assert!(blocklist.get::<VorbisComment>().is_some());
4562    ///
4563    /// // updating Vorbis Comment again reuses that same block
4564    /// blocklist.update::<VorbisComment>(
4565    ///     |vc| vc.insert(ARTIST, "Artist 2")
4566    /// );
4567    ///
4568    /// // the block now has two entries
4569    /// assert_eq!(
4570    ///     blocklist.get::<VorbisComment>()
4571    ///         .unwrap()
4572    ///         .all(ARTIST).collect::<Vec<_>>(),
4573    ///     vec!["Artist 1", "Artist 2"],
4574    /// );
4575    /// ```
4576    pub fn update<B>(&mut self, f: impl FnOnce(&mut B))
4577    where
4578        B: OptionalMetadataBlock + Default,
4579    {
4580        match self.get_mut() {
4581            Some(block) => f(block),
4582            None => {
4583                let mut b = B::default();
4584                f(&mut b);
4585                self.blocks.push(b.into());
4586            }
4587        }
4588    }
4589
4590    /// Sorts optional metadata blocks by block type
4591    ///
4592    /// The function converts the type to some key which is
4593    /// used for ordering blocks from smallest to largest.
4594    ///
4595    /// The order of blocks of the same type is preserved.
4596    /// This is an important consideration for APPLICATION
4597    /// metadata blocks, which may contain foreign metadata
4598    /// chunks that must be re-applied in the same order.
4599    ///
4600    /// # Example
4601    ///
4602    /// ```
4603    /// use flac_codec::metadata::{
4604    ///     BlockList, Streaminfo, Application, Padding, AsBlockRef,
4605    ///     OptionalBlockType,
4606    /// };
4607    ///
4608    /// // build a BlockList with a dummy Streaminfo
4609    /// let streaminfo = Streaminfo {
4610    ///     minimum_block_size: 0,
4611    ///     maximum_block_size: 0,
4612    ///     minimum_frame_size: None,
4613    ///     maximum_frame_size: None,
4614    ///     sample_rate: 44100,
4615    ///     channels: 1u8.try_into().unwrap(),
4616    ///     bits_per_sample: 16u32.try_into().unwrap(),
4617    ///     total_samples: None,
4618    ///     md5: None,
4619    /// };
4620    ///
4621    /// let mut blocklist = BlockList::new(streaminfo.clone());
4622    ///
4623    /// // add some blocks
4624    /// let application_1 = Application {
4625    ///     id: 0x1234,
4626    ///     data: vec![0x01, 0x02, 0x03, 0x04],
4627    /// };
4628    /// blocklist.insert(application_1.clone());
4629    ///
4630    /// let padding = Padding {
4631    ///     size: 10u32.try_into().unwrap(),
4632    /// };
4633    /// blocklist.insert(padding.clone());
4634    ///
4635    /// let application_2 = Application {
4636    ///     id: 0x6789,
4637    ///     data: vec![0x06, 0x07, 0x08, 0x09],
4638    /// };
4639    /// blocklist.insert(application_2.clone());
4640    ///
4641    /// // check their inital order
4642    /// let mut iter = blocklist.blocks();
4643    /// assert_eq!(iter.next(), Some(streaminfo.as_block_ref()));
4644    /// assert_eq!(iter.next(), Some(application_1.as_block_ref()));
4645    /// assert_eq!(iter.next(), Some(padding.as_block_ref()));
4646    /// assert_eq!(iter.next(), Some(application_2.as_block_ref()));
4647    /// assert_eq!(iter.next(), None);
4648    /// drop(iter);
4649    ///
4650    /// // sort the blocks to put padding last
4651    /// blocklist.sort_by(|t| match t {
4652    ///     OptionalBlockType::Application => 0,
4653    ///     OptionalBlockType::SeekTable => 1,
4654    ///     OptionalBlockType::VorbisComment => 2,
4655    ///     OptionalBlockType::Cuesheet => 3,
4656    ///     OptionalBlockType::Picture => 4,
4657    ///     OptionalBlockType::Padding => 5,
4658    /// });
4659    ///
4660    /// // re-check their new order
4661    /// let mut iter = blocklist.blocks();
4662    /// assert_eq!(iter.next(), Some(streaminfo.as_block_ref()));
4663    /// assert_eq!(iter.next(), Some(application_1.as_block_ref()));
4664    /// assert_eq!(iter.next(), Some(application_2.as_block_ref()));
4665    /// assert_eq!(iter.next(), Some(padding.as_block_ref()));
4666    /// assert_eq!(iter.next(), None);
4667    /// ```
4668    pub fn sort_by<O: Ord>(&mut self, f: impl Fn(OptionalBlockType) -> O) {
4669        self.blocks
4670            .sort_by_key(|block| f(block.optional_block_type()));
4671    }
4672}
4673
4674impl Metadata for BlockList {
4675    fn channel_count(&self) -> u8 {
4676        self.streaminfo.channels.get()
4677    }
4678
4679    fn channel_mask(&self) -> ChannelMask {
4680        use fields::CHANNEL_MASK;
4681
4682        self.get::<VorbisComment>()
4683            .and_then(|c| c.get(CHANNEL_MASK).and_then(|m| m.parse().ok()))
4684            .unwrap_or(ChannelMask::from_channels(self.channel_count()))
4685    }
4686
4687    fn sample_rate(&self) -> u32 {
4688        self.streaminfo.sample_rate
4689    }
4690
4691    fn bits_per_sample(&self) -> u32 {
4692        self.streaminfo.bits_per_sample.into()
4693    }
4694
4695    fn total_samples(&self) -> Option<u64> {
4696        self.streaminfo.total_samples.map(|s| s.get())
4697    }
4698
4699    fn md5(&self) -> Option<&[u8; 16]> {
4700        self.streaminfo.md5.as_ref()
4701    }
4702}
4703
4704impl FromIterator<Block> for Result<BlockList, Error> {
4705    fn from_iter<T: IntoIterator<Item = Block>>(iter: T) -> Self {
4706        let mut iter = iter.into_iter();
4707
4708        let mut list = match iter.next() {
4709            Some(Block::Streaminfo(streaminfo)) => BlockList::new(streaminfo),
4710            Some(_) | None => return Err(Error::MissingStreaminfo),
4711        };
4712
4713        for block in iter {
4714            match block {
4715                Block::Streaminfo(_) => return Err(Error::MultipleStreaminfo),
4716                Block::Padding(p) => {
4717                    list.insert(p);
4718                }
4719                Block::Application(p) => {
4720                    list.insert(p);
4721                }
4722                Block::SeekTable(p) => {
4723                    list.insert(p);
4724                }
4725                Block::VorbisComment(p) => {
4726                    list.insert(p);
4727                }
4728                Block::Cuesheet(p) => {
4729                    list.insert(p);
4730                }
4731                Block::Picture(p) => {
4732                    list.insert(p);
4733                }
4734            }
4735        }
4736
4737        Ok(list)
4738    }
4739}
4740
4741impl IntoIterator for BlockList {
4742    type Item = Block;
4743    type IntoIter = Box<dyn Iterator<Item = Block>>;
4744
4745    fn into_iter(self) -> Self::IntoIter {
4746        Box::new(
4747            std::iter::once(self.streaminfo.into())
4748                .chain(self.blocks.into_iter().map(|b| b.into())),
4749        )
4750    }
4751}
4752
4753impl<B: OptionalMetadataBlock> Extend<B> for BlockList {
4754    fn extend<I>(&mut self, iter: I)
4755    where
4756        I: IntoIterator<Item = B>,
4757    {
4758        for block in iter {
4759            self.insert(block);
4760        }
4761    }
4762}
4763
4764/// A type of FLAC metadata block which is not required
4765///
4766/// The STREAMINFO block is required.  All others are optional.
4767pub trait OptionalMetadataBlock: MetadataBlock + private::OptionalMetadataBlock {
4768    /// Our optional block type
4769    const OPTIONAL_TYPE: OptionalBlockType;
4770}
4771
4772/// A type of optional FLAC metadata block which is portable
4773///
4774/// These are blocks which may be safely ported from one
4775/// encoding of a FLAC file to another.
4776///
4777/// All blocks except STREAMINFO and SEEKTABLE are considered portable.
4778pub trait PortableMetadataBlock: OptionalMetadataBlock {}
4779
4780impl PortableMetadataBlock for Padding {}
4781impl PortableMetadataBlock for Application {}
4782impl PortableMetadataBlock for VorbisComment {}
4783impl PortableMetadataBlock for Cuesheet {}
4784impl PortableMetadataBlock for Picture {}
4785
4786mod private {
4787    use super::{
4788        Application, AsBlockRef, Block, BlockRef, BlockType, Cuesheet, OptionalBlockType, Padding,
4789        Picture, SeekTable, Streaminfo, VorbisComment,
4790    };
4791
4792    #[derive(Clone, Debug)]
4793    pub enum OptionalBlock {
4794        Padding(Padding),
4795        Application(Application),
4796        SeekTable(SeekTable),
4797        VorbisComment(VorbisComment),
4798        Cuesheet(Cuesheet),
4799        Picture(Picture),
4800    }
4801
4802    impl OptionalBlock {
4803        pub fn block_type(&self) -> BlockType {
4804            match self {
4805                Self::Padding(_) => BlockType::Padding,
4806                Self::Application(_) => BlockType::Application,
4807                Self::SeekTable(_) => BlockType::SeekTable,
4808                Self::VorbisComment(_) => BlockType::VorbisComment,
4809                Self::Cuesheet(_) => BlockType::Cuesheet,
4810                Self::Picture(_) => BlockType::Picture,
4811            }
4812        }
4813
4814        pub fn optional_block_type(&self) -> OptionalBlockType {
4815            match self {
4816                Self::Padding(_) => OptionalBlockType::Padding,
4817                Self::Application(_) => OptionalBlockType::Application,
4818                Self::SeekTable(_) => OptionalBlockType::SeekTable,
4819                Self::VorbisComment(_) => OptionalBlockType::VorbisComment,
4820                Self::Cuesheet(_) => OptionalBlockType::Cuesheet,
4821                Self::Picture(_) => OptionalBlockType::Picture,
4822            }
4823        }
4824    }
4825
4826    impl From<OptionalBlock> for Block {
4827        fn from(block: OptionalBlock) -> Block {
4828            match block {
4829                OptionalBlock::Padding(p) => Block::Padding(p),
4830                OptionalBlock::Application(a) => Block::Application(a),
4831                OptionalBlock::SeekTable(s) => Block::SeekTable(s),
4832                OptionalBlock::VorbisComment(v) => Block::VorbisComment(v),
4833                OptionalBlock::Cuesheet(c) => Block::Cuesheet(c),
4834                OptionalBlock::Picture(p) => Block::Picture(p),
4835            }
4836        }
4837    }
4838
4839    impl TryFrom<Block> for OptionalBlock {
4840        type Error = Streaminfo;
4841
4842        fn try_from(block: Block) -> Result<Self, Streaminfo> {
4843            match block {
4844                Block::Streaminfo(s) => Err(s),
4845                Block::Padding(p) => Ok(OptionalBlock::Padding(p)),
4846                Block::Application(a) => Ok(OptionalBlock::Application(a)),
4847                Block::SeekTable(s) => Ok(OptionalBlock::SeekTable(s)),
4848                Block::VorbisComment(v) => Ok(OptionalBlock::VorbisComment(v)),
4849                Block::Cuesheet(c) => Ok(OptionalBlock::Cuesheet(c)),
4850                Block::Picture(p) => Ok(OptionalBlock::Picture(p)),
4851            }
4852        }
4853    }
4854
4855    impl AsBlockRef for OptionalBlock {
4856        fn as_block_ref(&self) -> BlockRef<'_> {
4857            match self {
4858                Self::Padding(p) => BlockRef::Padding(p),
4859                Self::Application(a) => BlockRef::Application(a),
4860                Self::SeekTable(s) => BlockRef::SeekTable(s),
4861                Self::VorbisComment(v) => BlockRef::VorbisComment(v),
4862                Self::Cuesheet(v) => BlockRef::Cuesheet(v),
4863                Self::Picture(p) => BlockRef::Picture(p),
4864            }
4865        }
4866    }
4867
4868    pub trait OptionalMetadataBlock: Into<OptionalBlock> + TryFrom<OptionalBlock> {
4869        fn try_from_opt_block(block: &OptionalBlock) -> Result<&Self, &OptionalBlock>;
4870
4871        fn try_from_opt_block_mut(
4872            block: &mut OptionalBlock,
4873        ) -> Result<&mut Self, &mut OptionalBlock>;
4874    }
4875}
4876
4877/// The channel mask
4878///
4879/// This field is used to communicate that the channels
4880/// in the file differ from FLAC's default channel assignment
4881/// definitions.
4882///
4883/// It is generally used for multi-channel audio
4884/// and stored within the [`VorbisComment`] metadata block
4885/// as the [`fields::CHANNEL_MASK`] field.
4886///
4887/// # Example
4888///
4889/// ```
4890/// use flac_codec::metadata::{ChannelMask, Channel};
4891///
4892/// let mask = "0x003F".parse::<ChannelMask>().unwrap();
4893///
4894/// let mut channels = mask.channels();
4895/// assert_eq!(channels.next(), Some(Channel::FrontLeft));
4896/// assert_eq!(channels.next(), Some(Channel::FrontRight));
4897/// assert_eq!(channels.next(), Some(Channel::FrontCenter));
4898/// assert_eq!(channels.next(), Some(Channel::Lfe));
4899/// assert_eq!(channels.next(), Some(Channel::BackLeft));
4900/// assert_eq!(channels.next(), Some(Channel::BackRight));
4901/// assert_eq!(channels.next(), None);
4902/// ```
4903#[derive(Copy, Clone, Debug, Default)]
4904pub struct ChannelMask {
4905    mask: u32,
4906}
4907
4908impl ChannelMask {
4909    /// Iterates over all the mask's defined channels
4910    pub fn channels(&self) -> impl Iterator<Item = Channel> {
4911        [
4912            Channel::FrontLeft,
4913            Channel::FrontRight,
4914            Channel::FrontCenter,
4915            Channel::Lfe,
4916            Channel::BackLeft,
4917            Channel::BackRight,
4918            Channel::FrontLeftOfCenter,
4919            Channel::FrontRightOfCenter,
4920            Channel::BackCenter,
4921            Channel::SideLeft,
4922            Channel::SideRight,
4923            Channel::TopCenter,
4924            Channel::TopFrontLeft,
4925            Channel::TopFrontCenter,
4926            Channel::TopFrontRight,
4927            Channel::TopRearLeft,
4928            Channel::TopRearCenter,
4929            Channel::TopRearRight,
4930        ]
4931        .into_iter()
4932        .filter(|channel| (*channel as u32 & self.mask) != 0)
4933    }
4934
4935    fn from_channels(channels: u8) -> Self {
4936        match channels {
4937            1 => Self {
4938                mask: Channel::FrontCenter as u32,
4939            },
4940            2 => Self {
4941                mask: Channel::FrontLeft as u32 | Channel::FrontRight as u32,
4942            },
4943            3 => Self {
4944                mask: Channel::FrontLeft as u32
4945                    | Channel::FrontRight as u32
4946                    | Channel::FrontCenter as u32,
4947            },
4948            4 => Self {
4949                mask: Channel::FrontLeft as u32
4950                    | Channel::FrontRight as u32
4951                    | Channel::BackLeft as u32
4952                    | Channel::BackRight as u32,
4953            },
4954            5 => Self {
4955                mask: Channel::FrontLeft as u32
4956                    | Channel::FrontRight as u32
4957                    | Channel::FrontCenter as u32
4958                    | Channel::SideLeft as u32
4959                    | Channel::SideRight as u32,
4960            },
4961            6 => Self {
4962                mask: Channel::FrontLeft as u32
4963                    | Channel::FrontRight as u32
4964                    | Channel::FrontCenter as u32
4965                    | Channel::Lfe as u32
4966                    | Channel::SideLeft as u32
4967                    | Channel::SideRight as u32,
4968            },
4969            7 => Self {
4970                mask: Channel::FrontLeft as u32
4971                    | Channel::FrontRight as u32
4972                    | Channel::FrontCenter as u32
4973                    | Channel::Lfe as u32
4974                    | Channel::BackCenter as u32
4975                    | Channel::SideLeft as u32
4976                    | Channel::SideRight as u32,
4977            },
4978            8 => Self {
4979                mask: Channel::FrontLeft as u32
4980                    | Channel::FrontRight as u32
4981                    | Channel::FrontCenter as u32
4982                    | Channel::Lfe as u32
4983                    | Channel::BackLeft as u32
4984                    | Channel::BackRight as u32
4985                    | Channel::SideLeft as u32
4986                    | Channel::SideRight as u32,
4987            },
4988            // FLAC files are limited to 1-8 channels
4989            _ => panic!("undefined channel count"),
4990        }
4991    }
4992}
4993
4994impl std::str::FromStr for ChannelMask {
4995    type Err = ();
4996
4997    fn from_str(s: &str) -> Result<Self, Self::Err> {
4998        match s.split_once('x').ok_or(())? {
4999            ("0", hex) => u32::from_str_radix(hex, 16)
5000                .map(|mask| ChannelMask { mask })
5001                .map_err(|_| ()),
5002            _ => Err(()),
5003        }
5004    }
5005}
5006
5007impl std::fmt::Display for ChannelMask {
5008    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5009        write!(f, "0x{:04x}", self.mask)
5010    }
5011}
5012
5013impl From<ChannelMask> for u32 {
5014    fn from(mask: ChannelMask) -> u32 {
5015        mask.mask
5016    }
5017}
5018
5019impl From<u32> for ChannelMask {
5020    fn from(mask: u32) -> ChannelMask {
5021        ChannelMask { mask }
5022    }
5023}
5024
5025/// An individual channel mask channel
5026#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
5027pub enum Channel {
5028    /// Front left channel
5029    FrontLeft = 0b1,
5030
5031    /// Front right channel
5032    FrontRight = 0b10,
5033
5034    /// Front center channel
5035    FrontCenter = 0b100,
5036
5037    /// Low-frequency effects (LFE) channel
5038    Lfe = 0b1000,
5039
5040    /// Back left channel
5041    BackLeft = 0b10000,
5042
5043    /// Back right channel
5044    BackRight = 0b100000,
5045
5046    /// Front left of center channel
5047    FrontLeftOfCenter = 0b1000000,
5048
5049    /// Front right of center channel
5050    FrontRightOfCenter = 0b10000000,
5051
5052    /// Back center channel
5053    BackCenter = 0b100000000,
5054
5055    /// Side left channel
5056    SideLeft = 0b1000000000,
5057
5058    /// Side right channel
5059    SideRight = 0b10000000000,
5060
5061    /// Top center channel
5062    TopCenter = 0b100000000000,
5063
5064    /// Top front left channel
5065    TopFrontLeft = 0b1000000000000,
5066
5067    /// Top front center channel
5068    TopFrontCenter = 0b10000000000000,
5069
5070    /// Top front right channel
5071    TopFrontRight = 0b100000000000000,
5072
5073    /// Top rear left channel
5074    TopRearLeft = 0b1000000000000000,
5075
5076    /// Top rear center channel
5077    TopRearCenter = 0b10000000000000000,
5078
5079    /// Top rear right channel
5080    TopRearRight = 0b100000000000000000,
5081}
5082
5083impl std::fmt::Display for Channel {
5084    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
5085        match self {
5086            Self::FrontLeft => "front left".fmt(f),
5087            Self::FrontRight => "front right".fmt(f),
5088            Self::FrontCenter => "front center".fmt(f),
5089            Self::Lfe => "LFE".fmt(f),
5090            Self::BackLeft => "back left".fmt(f),
5091            Self::BackRight => "back right".fmt(f),
5092            Self::FrontLeftOfCenter => "front left of center".fmt(f),
5093            Self::FrontRightOfCenter => "front right of center".fmt(f),
5094            Self::BackCenter => "back center".fmt(f),
5095            Self::SideLeft => "side left".fmt(f),
5096            Self::SideRight => "side right".fmt(f),
5097            Self::TopCenter => "top center".fmt(f),
5098            Self::TopFrontLeft => "top front left".fmt(f),
5099            Self::TopFrontCenter => "top front center".fmt(f),
5100            Self::TopFrontRight => "top front right".fmt(f),
5101            Self::TopRearLeft => "top rear left".fmt(f),
5102            Self::TopRearCenter => "top rear center".fmt(f),
5103            Self::TopRearRight => "top rear right".fmt(f),
5104        }
5105    }
5106}