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