Skip to content

Commit 062ec3e

Browse files
committed
Probe: Add read_bound()
To get a `BoundTaggedFile` from a `Probe`. Plan to use this to simplify some tests. Signed-off-by: Serial <69764315+Serial-ATA@users.noreply.github.com>
1 parent 4dea59e commit 062ec3e

File tree

3 files changed

+72
-27
lines changed

3 files changed

+72
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- For example, a track has `ItemKey::TrackArtist` = "Foo & Bar", then `ItemKey::AlbumArtists` = ["Foo", "Bar"].
1313
- **Serde**: [Serde] support for `*Type` enums (`FileType`, `TagType`, `PictureType`)
1414
- Support can be enabled with the new `serde` feature (not enabled by default)
15+
- **Probe**: `Probe::read_bound()` ([PR](https://github.com/Serial-ATA/lofty-rs/pull/557))
16+
- Same as `Probe::read()`, but returns a [`BoundTaggedFile`](https://docs.rs/lofty/latest/lofty/file/struct.BoundTaggedFile.html)
1517

1618
### Changed
1719
- **ID3v2**: Check `TXXX:ALBUMARTIST` and `TXXX:ALBUM ARTIST` for `ItemKey::AlbumArtist` conversions

lofty/src/file/tagged_file.rs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ use crate::config::{ParseOptions, WriteOptions};
44
use crate::error::{LoftyError, Result};
55
use crate::properties::FileProperties;
66
use crate::tag::{Tag, TagExt, TagType};
7-
87
use crate::util::io::{FileLike, Length, Truncate};
9-
use std::fs::File;
8+
109
use std::io::{Read, Seek};
1110

1211
/// Provides a common interface between [`TaggedFile`] and [`BoundTaggedFile`]
@@ -453,13 +452,13 @@ impl AudioFile for TaggedFile {
453452
}
454453
}
455454

456-
impl From<BoundTaggedFile> for TaggedFile {
457-
fn from(input: BoundTaggedFile) -> Self {
455+
impl<F> From<BoundTaggedFile<F>> for TaggedFile {
456+
fn from(input: BoundTaggedFile<F>) -> Self {
458457
input.inner
459458
}
460459
}
461460

462-
/// A variant of [`TaggedFile`] that holds a [`File`] handle, and reflects changes
461+
/// A variant of [`TaggedFile`] that holds a handle to its original [`FileLike`] buffer, and reflects changes
463462
/// such as tag removals.
464463
///
465464
/// For example:
@@ -515,12 +514,27 @@ impl From<BoundTaggedFile> for TaggedFile {
515514
/// assert!(!bound_tagged_file.contains_tag_type(TagType::Id3v2));
516515
/// # Ok(()) }
517516
/// ```
518-
pub struct BoundTaggedFile {
519-
inner: TaggedFile,
520-
file_handle: File,
517+
pub struct BoundTaggedFile<F> {
518+
pub(crate) inner: TaggedFile,
519+
pub(crate) file_handle: F,
521520
}
522521

523-
impl BoundTaggedFile {
522+
impl<F> BoundTaggedFile<F> {
523+
/// Consume this tagged file and return the internal file "buffer".
524+
/// This allows you to reuse the internal file.
525+
///
526+
/// Any changes that haven't been commited will be discarded once you
527+
/// call this function.
528+
pub fn into_inner(self) -> F {
529+
self.file_handle
530+
}
531+
}
532+
533+
impl<F: FileLike> BoundTaggedFile<F>
534+
where
535+
LoftyError: From<<F as Truncate>::Error>,
536+
LoftyError: From<<F as Length>::Error>,
537+
{
524538
/// Create a new [`BoundTaggedFile`]
525539
///
526540
/// # Errors
@@ -544,7 +558,7 @@ impl BoundTaggedFile {
544558
/// let bound_tagged_file = BoundTaggedFile::read_from(file, parse_options)?;
545559
/// # Ok(()) }
546560
/// ```
547-
pub fn read_from(mut file: File, parse_options: ParseOptions) -> Result<Self> {
561+
pub fn read_from(mut file: F, parse_options: ParseOptions) -> Result<Self> {
548562
let inner = TaggedFile::read_from(&mut file, parse_options)?;
549563
file.rewind()?;
550564

@@ -588,18 +602,9 @@ impl BoundTaggedFile {
588602

589603
Ok(())
590604
}
591-
592-
/// Consume this tagged file and return the internal file "buffer".
593-
/// This allows you to reuse the internal file.
594-
///
595-
/// Any changes that haven't been commited will be discarded once you
596-
/// call this function.
597-
pub fn into_inner(self) -> File {
598-
self.file_handle
599-
}
600605
}
601606

602-
impl TaggedFileExt for BoundTaggedFile {
607+
impl<F> TaggedFileExt for BoundTaggedFile<F> {
603608
fn file_type(&self) -> FileType {
604609
self.inner.file_type()
605610
}
@@ -633,7 +638,7 @@ impl TaggedFileExt for BoundTaggedFile {
633638
}
634639
}
635640

636-
impl AudioFile for BoundTaggedFile {
641+
impl<T> AudioFile for BoundTaggedFile<T> {
637642
type Properties = FileProperties;
638643

639644
fn read_from<R>(_: &mut R, _: ParseOptions) -> Result<Self>

lofty/src/probe.rs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::aac::AacFile;
44
use crate::ape::ApeFile;
55
use crate::config::{ParseOptions, global_options};
66
use crate::error::Result;
7-
use crate::file::{AudioFile, FileType, FileTypeGuessResult, TaggedFile};
7+
use crate::file::{AudioFile, BoundTaggedFile, FileType, FileTypeGuessResult, TaggedFile};
88
use crate::flac::FlacFile;
99
use crate::iff::aiff::AiffFile;
1010
use crate::iff::wav::WavFile;
@@ -19,6 +19,7 @@ use crate::ogg::vorbis::VorbisFile;
1919
use crate::resolve::custom_resolvers;
2020
use crate::wavpack::WavPackFile;
2121

22+
use crate::io::FileLike;
2223
use std::fs::File;
2324
use std::io::{BufReader, Cursor, Read, Seek, SeekFrom};
2425
use std::path::Path;
@@ -455,16 +456,20 @@ impl<R: Read + Seek> Probe<R> {
455456
/// let parsed_file = probe.read()?;
456457
/// # Ok(()) }
457458
/// ```
458-
pub fn read(mut self) -> Result<TaggedFile> {
459+
pub fn read(self) -> Result<TaggedFile> {
460+
self.read_inner().map(|(tagged_file, _)| tagged_file)
461+
}
462+
463+
fn read_inner(mut self) -> Result<(TaggedFile, R)> {
459464
let reader = &mut self.inner;
460465
let options = self.options.unwrap_or_default();
461466

462467
if !options.read_tags && !options.read_properties {
463468
log::warn!("Skipping both tag and property reading, file will be empty");
464469
}
465470

466-
match self.f_ty {
467-
Some(f_type) => Ok(match f_type {
471+
let tagged_file = match self.f_ty {
472+
Some(f_type) => match f_type {
468473
FileType::Aac => AacFile::read_from(reader, options)?.into(),
469474
FileType::Aiff => AiffFile::read_from(reader, options)?.into(),
470475
FileType::Ape => ApeFile::read_from(reader, options)?.into(),
@@ -485,9 +490,42 @@ impl<R: Read + Seek> Probe<R> {
485490
let resolver = crate::resolve::lookup_resolver(c);
486491
resolver.read_from(reader, options)?
487492
},
488-
}),
493+
},
489494
None => err!(UnknownFormat),
490-
}
495+
};
496+
497+
Ok((tagged_file, self.inner))
498+
}
499+
}
500+
501+
impl<F: FileLike> Probe<F> {
502+
/// Attempts to extract a [`BoundTaggedFile`] from the reader
503+
///
504+
/// # Errors
505+
///
506+
/// See [`Self::read()`].
507+
///
508+
/// # Examples
509+
///
510+
/// ```rust
511+
/// use lofty::file::FileType;
512+
/// use lofty::probe::Probe;
513+
///
514+
/// # fn main() -> lofty::error::Result<()> {
515+
/// # let path = "tests/files/assets/minimal/full_test.mp3";
516+
/// # let file = std::fs::File::open(path)?;
517+
/// let probe = Probe::new(file).guess_file_type()?;
518+
///
519+
/// let bound_tagged_file = probe.read_bound()?;
520+
/// # Ok(()) }
521+
/// ```
522+
pub fn read_bound(self) -> Result<BoundTaggedFile<F>> {
523+
let (tagged_file, file_handle) = self.read_inner()?;
524+
525+
Ok(BoundTaggedFile {
526+
inner: tagged_file,
527+
file_handle,
528+
})
491529
}
492530
}
493531

0 commit comments

Comments
 (0)